diff --git a/changelogs/unreleased/gh-8104-memtx-mvcc-assertion-failure-during-stmt-preparation.md b/changelogs/unreleased/gh-8104-memtx-mvcc-assertion-failure-during-stmt-preparation.md
new file mode 100644
index 0000000000000000000000000000000000000000..2f2c6878ae1fc48b44dc1d40fba97f3da8e17eb2
--- /dev/null
+++ b/changelogs/unreleased/gh-8104-memtx-mvcc-assertion-failure-during-stmt-preparation.md
@@ -0,0 +1,3 @@
+## bugfix/memtx
+
+* Fixed assertion failure in MVCC during statement preparation (gh-8104).
diff --git a/src/box/memtx_tx.c b/src/box/memtx_tx.c
index 183a725c72047c6212b044a57572fbb3449aa6e1..e309cfea92246c85a66a73a8ccc08461a8d6643c 100644
--- a/src/box/memtx_tx.c
+++ b/src/box/memtx_tx.c
@@ -2409,14 +2409,6 @@ memtx_tx_history_prepare_insert_stmt(struct txn_stmt *stmt)
 	uint32_t index_count = story->index_count;
 	memtx_tx_history_sink_story(story);
 
-	struct memtx_story *old_story = story->link[0].older_story;
-	if (stmt->del_story == NULL)
-		assert(old_story == NULL || old_story->rollbacked ||
-		       old_story->del_psn != 0);
-	else
-		assert(old_story != NULL &&
-		       (old_story->rollbacked || stmt->del_story == old_story));
-
 	if (stmt->del_story == NULL) {
 		/*
 		 * This statement replaced nothing. That means that before
@@ -2449,15 +2441,26 @@ memtx_tx_history_prepare_insert_stmt(struct txn_stmt *stmt)
 		}
 	}
 
-	if (old_story != NULL && old_story->rollbacked) {
-		assert(old_story->link[0].older_story == NULL);
-		old_story = NULL;
+	struct memtx_story *old_story = story->link[0].older_story;
+	if (stmt->del_story == NULL)
+		assert(old_story == NULL || old_story->rollbacked ||
+		       old_story->del_psn != 0);
+	else
+		assert(old_story != NULL &&
+		       (old_story->rollbacked || stmt->del_story == old_story));
+	if (old_story != NULL) {
+		if (old_story->rollbacked) {
+			assert(old_story->link[0].older_story == NULL);
+			old_story = NULL;
+		} else if (old_story->del_psn != 0) {
+			assert(stmt->del_story == NULL);
+			old_story = NULL;
+		}
 	}
-
 	if (old_story != NULL) {
 		/*
 		 * There can be some transactions that want to delete old_story.
-		 * It can be this transaction, or some other prepared TX.
+		 * It can be this transaction.
 		 * All other transactions must be aborted or relinked to delete
 		 * this tuple.
 		 */
diff --git a/test/box-luatest/gh_8104_memtx_mvcc_assertion_failure_during_stmt_preparation_test.lua b/test/box-luatest/gh_8104_memtx_mvcc_assertion_failure_during_stmt_preparation_test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..1d8859669eed9272a08956f5bb4a976e3075013e
--- /dev/null
+++ b/test/box-luatest/gh_8104_memtx_mvcc_assertion_failure_during_stmt_preparation_test.lua
@@ -0,0 +1,49 @@
+local server = require('luatest.server')
+local t = require('luatest')
+
+local g = t.group()
+
+g.before_all(function(cg)
+    cg.server = server:new {
+        alias   = 'dflt',
+        box_cfg = {memtx_use_mvcc_engine = true}
+    }
+    cg.server:start()
+    cg.server:exec(function()
+        local s = box.schema.create_space('s')
+        s:create_index('pk')
+    end)
+end)
+
+g.after_all(function(cg)
+    cg.server:drop()
+end)
+
+-- Checks that preparation of an insert statement with an older story deleted by
+-- a prepared transaction does not fail assertion.
+g.test_preparation_with_deleted_older_story_assertion = function(cg)
+    cg.server:exec(function()
+        local fiber = require('fiber')
+        local t = require('luatest')
+
+        box.space.s:replace{0}
+
+        local first_replace = fiber.create(function()
+            fiber.self():set_joinable(true)
+            box.atomic(function()
+                box.space.s:delete{0}
+            end)
+        end)
+        local second_replace = fiber.create(function()
+            fiber.self():set_joinable(true)
+            box.atomic(function()
+                box.space.s:insert{0}
+            end)
+        end)
+
+        first_replace:join()
+        second_replace:join()
+
+        t.assert_equals(box.space.s:select{}, {{0}})
+    end)
+end