diff --git a/src/core/lib/channel/handshaker.c b/src/core/lib/channel/handshaker.c
index f3bd91284ee1edcf65893bfd259c35f57e3a7c60..927723499a566971fb1abd770e030069fd33ca56 100644
--- a/src/core/lib/channel/handshaker.c
+++ b/src/core/lib/channel/handshaker.c
@@ -141,7 +141,8 @@ void grpc_handshake_manager_destroy(grpc_exec_ctx* exec_ctx,
 void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx,
                                      grpc_handshake_manager* mgr) {
   gpr_mu_lock(&mgr->mu);
-  if (mgr->index > 0) {
+  // Shutdown the handshaker that's currently in progress, if any.
+  if (mgr->index > 0 && mgr->index <= mgr->count) {
     grpc_handshaker_shutdown(exec_ctx, mgr->handshakers[mgr->index - 1]);
   }
   gpr_mu_unlock(&mgr->mu);
@@ -149,28 +150,27 @@ void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx,
 
 // Helper function to call either the next handshaker or the
 // on_handshake_done callback.
-static void call_next_handshaker_locked(grpc_exec_ctx* exec_ctx,
+// Returns true if we've scheduled the on_handshake_done callback.
+static bool call_next_handshaker_locked(grpc_exec_ctx* exec_ctx,
                                         grpc_handshake_manager* mgr,
                                         grpc_error* error) {
   GPR_ASSERT(mgr->index <= mgr->count);
   // If we got an error or we've finished the last handshaker, invoke
   // the on_handshake_done callback.  Otherwise, call the next handshaker.
+  bool done = false;
   if (error != GRPC_ERROR_NONE || mgr->index == mgr->count) {
     // Cancel deadline timer, since we're invoking the on_handshake_done
     // callback now.
     grpc_timer_cancel(exec_ctx, &mgr->deadline_timer);
     grpc_exec_ctx_sched(exec_ctx, &mgr->on_handshake_done, error, NULL);
-    // Since we're invoking the final callback, we won't be coming back
-    // to this function, so we can release our reference to the
-    // handshake manager.
-    grpc_handshake_manager_unref(exec_ctx, mgr);
-    return;
+    done = true;
+  } else {
+    grpc_handshaker_do_handshake(exec_ctx, mgr->handshakers[mgr->index],
+                                 mgr->acceptor, &mgr->call_next_handshaker,
+                                 &mgr->args);
   }
-  // Call the next handshaker.
-  grpc_handshaker_do_handshake(exec_ctx, mgr->handshakers[mgr->index],
-                               mgr->acceptor, &mgr->call_next_handshaker,
-                               &mgr->args);
   ++mgr->index;
+  return done;
 }
 
 // A function used as the handshaker-done callback when chaining
@@ -179,8 +179,14 @@ static void call_next_handshaker(grpc_exec_ctx* exec_ctx, void* arg,
                                  grpc_error* error) {
   grpc_handshake_manager* mgr = arg;
   gpr_mu_lock(&mgr->mu);
-  call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_REF(error));
+  bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_REF(error));
   gpr_mu_unlock(&mgr->mu);
+  // If we're invoked the final callback, we won't be coming back
+  // to this function, so we can release our reference to the
+  // handshake manager.
+  if (done) {
+    grpc_handshake_manager_unref(exec_ctx, mgr);
+  }
 }
 
 // Callback invoked when deadline is exceeded.
@@ -217,6 +223,9 @@ void grpc_handshake_manager_do_handshake(
                   on_timeout, mgr, gpr_now(GPR_CLOCK_MONOTONIC));
   // Start first handshaker, which also owns a ref.
   gpr_ref(&mgr->refs);
-  call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_NONE);
+  bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_NONE);
   gpr_mu_unlock(&mgr->mu);
+  if (done) {
+    grpc_handshake_manager_unref(exec_ctx, mgr);
+  }
 }