diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index 08087cfe46586cba582f66f08b5b98f40d099db7..3acdef6c87526428ef2fae53f73db43b3fcef9b4 100644
--- a/src/lib/core/fiber.c
+++ b/src/lib/core/fiber.c
@@ -528,14 +528,19 @@ fiber_wakeup(struct fiber *f)
 void
 fiber_cancel(struct fiber *f)
 {
-	assert(f->fid != 0);
 	/**
 	 * Do nothing if the fiber is dead, since cancelling
 	 * the fiber would clear the diagnostics area and
 	 * the cause of death would be lost.
 	 */
-	if (fiber_is_dead(f))
+	if (fiber_is_dead(f)) {
+		if ((f->flags & FIBER_IS_JOINABLE) == 0) {
+			panic("Cancel of a finished and already "
+			      "recycled fiber");
+		}
+		assert(f->fid != 0);
 		return;
+	}
 
 	f->flags |= FIBER_IS_CANCELLED;
 
@@ -662,6 +667,9 @@ fiber_join_timeout(struct fiber *fiber, double timeout)
 		diag_set(TimedOut);
 		return -1;
 	}
+	assert((fiber->flags & FIBER_IS_RUNNING) == 0);
+	assert((fiber->flags & FIBER_IS_JOINABLE) != 0);
+	fiber->flags &= ~FIBER_IS_JOINABLE;
 
 	/* Move exception to the caller */
 	int ret = fiber->f_ret;
@@ -877,11 +885,6 @@ fiber_reset(struct fiber *fiber)
 {
 	rlist_create(&fiber->on_yield);
 	rlist_create(&fiber->on_stop);
-	/*
-	 * Preserve the running flag if set. Reset might be called on the
-	 * current fiber when it is recycled.
-	 */
-	fiber->flags = FIBER_DEFAULT_FLAGS | (fiber->flags & FIBER_IS_RUNNING);
 #if ENABLE_FIBER_TOP
 	clock_stat_reset(&fiber->clock_stat);
 #endif /* ENABLE_FIBER_TOP */
@@ -891,6 +894,7 @@ fiber_reset(struct fiber *fiber)
 static void
 fiber_recycle(struct fiber *fiber)
 {
+	assert((fiber->flags & FIBER_IS_DEAD) != 0);
 	/* no exceptions are leaking */
 	assert(diag_is_empty(&fiber->diag));
 	/* no pending wakeup */
@@ -1237,6 +1241,8 @@ fiber_new_ex(const char *name, const struct fiber_attr *fiber_attr,
 		fiber = rlist_first_entry(&cord->dead,
 					  struct fiber, link);
 		rlist_move_entry(&cord->alive, fiber, link);
+		assert((fiber->flags | FIBER_IS_DEAD) != 0);
+		fiber->flags = FIBER_DEFAULT_FLAGS;
 	} else {
 		fiber = (struct fiber *)
 			mempool_alloc(&cord->fiber_mempool);
@@ -1478,7 +1484,7 @@ cord_create(struct cord *cord, const char *name)
 	cord->sched.name = NULL;
 	fiber_set_name(&cord->sched, "sched");
 	cord->fiber = &cord->sched;
-	cord->sched.flags |= FIBER_IS_RUNNING;
+	cord->sched.flags = FIBER_IS_RUNNING;
 
 	cord->next_fid = FIBER_ID_MAX_RESERVED + 1;
 	/*
diff --git a/test/unit/fiber.cc b/test/unit/fiber.cc
index c0a46c6123c282e644bd3ffe28b457a60dba5579..9d21840dd1f646f04c9b7ec1e9ae01a4e233f9d6 100644
--- a/test/unit/fiber.cc
+++ b/test/unit/fiber.cc
@@ -246,6 +246,21 @@ fiber_wakeup_self_test()
 	footer();
 }
 
+static void
+fiber_dead_while_in_cache_test(void)
+{
+	header();
+
+	struct fiber *f = fiber_new_xc("nop", noop_f);
+	int fiber_count = fiber_count_total();
+	fiber_start(f);
+	/* The fiber remains in the cache of recycled fibers. */
+	fail_unless(fiber_count == fiber_count_total());
+	fail_unless(fiber_is_dead(f));
+
+	footer();
+}
+
 static int
 main_f(va_list ap)
 {
@@ -253,6 +268,7 @@ main_f(va_list ap)
 	fiber_join_test();
 	fiber_stack_test();
 	fiber_wakeup_self_test();
+	fiber_dead_while_in_cache_test();
 	ev_break(loop(), EVBREAK_ALL);
 	return 0;
 }
diff --git a/test/unit/fiber.result b/test/unit/fiber.result
index 320b258f5f446d7639e072db747129cd2c7ce19b..f85d7aca899a5c84fc05cf47b0c992c8f6a8857c 100644
--- a/test/unit/fiber.result
+++ b/test/unit/fiber.result
@@ -19,3 +19,5 @@ OutOfMemory: Failed to allocate 42 bytes in allocator for exception
 	*** fiber_stack_test: done ***
 	*** fiber_wakeup_self_test ***
 	*** fiber_wakeup_self_test: done ***
+	*** fiber_dead_while_in_cache_test ***
+	*** fiber_dead_while_in_cache_test: done ***