diff --git a/changelogs/unreleased/gh-8946-foreign-key-disables-truncate.md b/changelogs/unreleased/gh-8946-foreign-key-disables-truncate.md
new file mode 100644
index 0000000000000000000000000000000000000000..92dbfc5f4add8897fe2e82407940b57c647dfd6a
--- /dev/null
+++ b/changelogs/unreleased/gh-8946-foreign-key-disables-truncate.md
@@ -0,0 +1,4 @@
+## bugfix/core
+
+* Fixed a bug when a space that is referenced by a foreign key could not
+  be truncated even if the referring space was empty (gh-8946).
diff --git a/src/box/alter.cc b/src/box/alter.cc
index 9aa186fc439a1127bd9b963480d4e054001c99b1..f8784da6ed7b71a37b32507cacc8914cc5bb57ab 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -40,6 +40,7 @@
 #include "coll_id_def.h"
 #include "txn.h"
 #include "tuple.h"
+#include "tuple_constraint.h"
 #include "fiber.h" /* for gc_pool */
 #include "scoped_guard.h"
 #include <base64.h>
@@ -1951,6 +1952,42 @@ space_check_pinned(struct space *space)
 	return 0;
 }
 
+/**
+ * Check whether @a space holders prohibit truncate of the space.
+ * For example truncation in not allowed if another non-empty space refers
+ * to this space via foreign key link.
+ * Return 0 if allowed, or -1 if not allowed (diag is set).
+ */
+static int
+space_check_truncate(struct space *space)
+{
+	/* Check for foreign keys that refers to this space. */
+	struct space_cache_holder *h;
+	rlist_foreach_entry(h, &space->space_cache_pin_list, link) {
+		if (h->selfpin)
+			continue;
+		if (h->type != SPACE_HOLDER_FOREIGN_KEY)
+			continue;
+		struct tuple_constraint *constr =
+			container_of(h, struct tuple_constraint,
+				     space_cache_holder);
+		struct space *other_space = constr->space;
+		/*
+		 * If the referring space is empty then the truncate can't
+		 * break foreign key consistency.
+		 */
+		if (space_bsize(other_space) == 0)
+			continue;
+		const char *type_str =
+			space_cache_holder_type_strs[h->type];
+		diag_set(ClientError, ER_ALTER_SPACE,
+			 space_name(space),
+			 tt_sprintf("space is referenced by %s", type_str));
+		return -1;
+	}
+	return 0;
+}
+
 /**
  * A trigger which is invoked on replace in a data dictionary
  * space _space.
@@ -2404,11 +2441,10 @@ on_replace_dd_index(struct trigger * /* trigger */, void *event)
 				  "space sequence exists");
 			return -1;
 		}
-
 		/*
-		 * Must not truncate pinned space.
+		 * Check space's holders.
 		 */
-		if (space_check_pinned(old_space) != 0)
+		if (space_check_truncate(old_space) != 0)
 			return -1;
 	}
 
@@ -2667,7 +2703,8 @@ on_replace_dd_truncate(struct trigger * /* trigger */, void *event)
 		return -1;
 	}
 
-	if (space_check_pinned(old_space) != 0)
+	/* Check space's holders. */
+	if (space_check_truncate(old_space) != 0)
 		return -1;
 
 	struct alter_space *alter = alter_space_new(old_space);
diff --git a/src/box/space_cache.h b/src/box/space_cache.h
index 344f8359ec01f770ffdcf8279aebe6dc1bf46a22..97d64b52b1bf2294777699449d5c464659b0b258 100644
--- a/src/box/space_cache.h
+++ b/src/box/space_cache.h
@@ -176,7 +176,7 @@ space_cache_unpin(struct space_cache_holder *holder);
 /**
  * Check whether the @a space has holders or not.
  * If it has, @a type argument is set to the first holder's type.
- * The function must be in cache (asserted).
+ * The space must be in cache (asserted).
  * If a space has holders, it must not be deleted (asserted).
  */
 bool
diff --git a/test/engine-luatest/gh_6436_complex_foreign_key_test.lua b/test/engine-luatest/gh_6436_complex_foreign_key_test.lua
index e024a0882aa9281d4fd287451d1db1d7091ed30f..9a26e28516a5cb8694a964a1788a65a83db9af42 100644
--- a/test/engine-luatest/gh_6436_complex_foreign_key_test.lua
+++ b/test/engine-luatest/gh_6436_complex_foreign_key_test.lua
@@ -109,7 +109,7 @@ g.test_complex_foreign_key_primary = function(cg)
         t.assert_equals(country:select{}, {{1, 11, 'Russia'}, {1, 12, 'France'}})
         t.assert_error_msg_content_equals(
             "Can't modify space 'country': space is referenced by foreign key",
-            function() country:drop() end
+            box.atomic, country.drop, country
         )
         t.assert_error_msg_content_equals(
             "Foreign key 'country' integrity check failed: wrong foreign field name",
@@ -198,7 +198,7 @@ g.test_complex_foreign_key_secondary = function(cg)
                                            {101, 1, 'earth', 'rf', 'France'}})
         t.assert_error_msg_content_equals(
             "Can't modify space 'country': space is referenced by foreign key",
-            function() country:drop() end
+            box.atomic, country.drop, country
         )
         t.assert_equals(country:select{}, {{100, 1, 'earth', 'ru', 'Russia'},
                                            {101, 1, 'earth', 'rf', 'France'}})
@@ -293,7 +293,7 @@ g.test_complex_foreign_key_numeric = function(cg)
                                            {101, 1, 'earth', 'rf', 'France'}})
         t.assert_error_msg_content_equals(
             "Can't modify space 'country': space is referenced by foreign key",
-            function() country:drop() end
+            box.atomic, country.drop, country
         )
         t.assert_equals(country:select{}, {{100, 1, 'earth', 'ru', 'Russia'},
                                            {101, 1, 'earth', 'rf', 'France'}})
diff --git a/test/engine-luatest/gh_6436_field_foreign_key_test.lua b/test/engine-luatest/gh_6436_field_foreign_key_test.lua
index 133d2ea0c39126fc6020dedf125a148432eb3ed5..bdb04acde40606513d0fce15761061827655948e 100644
--- a/test/engine-luatest/gh_6436_field_foreign_key_test.lua
+++ b/test/engine-luatest/gh_6436_field_foreign_key_test.lua
@@ -137,7 +137,7 @@ g.test_foreign_key_primary = function(cg)
         t.assert_equals(country:select{}, {{1, 'ru', 'Russia'}, {2, 'fr', 'France'}})
         t.assert_error_msg_content_equals(
             "Can't modify space 'country': space is referenced by foreign key",
-            function() country:drop() end
+            box.atomic, country.drop, country
         )
         t.assert_equals(country:select{}, {{1, 'ru', 'Russia'}, {2, 'fr', 'France'}})
         t.assert_error_msg_content_equals(
@@ -215,7 +215,7 @@ g.test_foreign_key_secondary = function(cg)
         t.assert_equals(country:select{}, {{1, 'ru', 'Russia'}, {2, 'fr', 'France'}})
         t.assert_error_msg_content_equals(
             "Can't modify space 'country': space is referenced by foreign key",
-            function() country:drop() end
+            box.atomic, country.drop, country
         )
         t.assert_equals(country:select{}, {{1, 'ru', 'Russia'}, {2, 'fr', 'France'}})
         t.assert_error_msg_content_equals(
@@ -297,7 +297,7 @@ g.test_foreign_key_numeric = function(cg)
         t.assert_equals(country:select{}, {{1, 'ru', 'Russia'}, {2, 'fr', 'France'}})
         t.assert_error_msg_content_equals(
             "Can't modify space 'country': space is referenced by foreign key",
-            function() country:drop() end
+            box.atomic, country.drop, country
         )
         t.assert_equals(country:select{}, {{1, 'ru', 'Russia'}, {2, 'fr', 'France'}})
         t.assert_error_msg_content_equals(
diff --git a/test/engine-luatest/gh_8946_foreign_key_disables_truncate_test.lua b/test/engine-luatest/gh_8946_foreign_key_disables_truncate_test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..17c6d3f2c0b5c207a01b3b4833b5faf17cdea21f
--- /dev/null
+++ b/test/engine-luatest/gh_8946_foreign_key_disables_truncate_test.lua
@@ -0,0 +1,131 @@
+-- https://github.com/tarantool/tarantool/issues/8946
+-- Test that a space that another empty space refers to can be truncated.
+local server = require('luatest.server')
+local t = require('luatest')
+
+local engines = {{engine = 'memtx'}, {engine = 'vinyl'}}
+local g = t.group('gh-8946-foreign-key-truncate-test', engines)
+
+g.before_all(function(cg)
+    cg.server = server:new({alias = 'master'})
+    cg.server:start()
+end)
+
+g.after_all(function(cg)
+    cg.server:stop()
+    cg.server = nil
+end)
+
+g.after_each(function(cg)
+    cg.server:exec(function()
+        if box.space.client_phones then
+            box.space.client_phones:drop()
+        end
+        if box.space.client then
+            box.space.client:drop()
+        end
+    end)
+end)
+
+-- Field foreign key must not disable truncate if referring space is empty.
+g.test_field_foreign_key_truncate = function(cg)
+    local engine = cg.params.engine
+
+    cg.server:exec(function(engine)
+        box.schema.space.create('client', {engine = engine})
+        box.space.client:format({
+            {name = 'customer_id', type = 'string', is_nullable = false},
+            {name = 'esia_id', type = 'string', is_nullable = false},
+        })
+
+        box.space.client:create_index('pk_client_customer_id', {
+            parts = {{field = 'customer_id', collation = 'unicode'}},
+            type = 'tree', unique = true
+        })
+        box.space.client:create_index('idx_client_esia_id', {
+            parts = {{field = 'esia_id', collation = 'unicode'}},
+            type = 'tree', unique = false
+        })
+
+        box.schema.space.create('client_phones', {engine = engine})
+        box.space.client_phones:format({
+            {name = 'phone', type = 'string', is_nullable = false},
+            {name = 'customer_id',
+             foreign_key = {space = 'client', field = 'customer_id'}},
+        })
+
+        box.space.client_phones:create_index('idx_client_phones_phone', {
+            parts = {{field = 'phone', collation = 'unicode'}},
+            type = 'tree', unique = true
+        })
+
+        box.space.client:insert{'01','esia-01'}
+        box.space.client:insert{'02','esia-02'}
+
+        box.space.client_phones:insert{'9121234','01'}
+        box.space.client_phones:insert{'3222222','02'}
+
+        -- Now truncate is prohibited.
+        t.assert_error_msg_content_equals(
+            "Can't modify space 'client': space is referenced by foreign key",
+            box.space.client.truncate, box.space.client)
+
+        box.space.client_phones:truncate()
+
+        -- Now truncate is allowed.
+        box.space.client:truncate()
+    end, {engine})
+end
+
+-- Tuple foreign key must not disable truncate if referring space is empty.
+g.test_tuple_foreign_key_truncate = function(cg)
+    local engine = cg.params.engine
+
+    cg.server:exec(function(engine)
+        box.schema.space.create('client', {engine = engine})
+        box.space.client:format({
+            {name = 'customer_id', type = 'string', is_nullable = false},
+            {name = 'esia_id', type = 'string', is_nullable = false},
+        })
+
+        box.space.client:create_index('pk_client_customer_id', {
+            parts = {{field = 'customer_id', collation = 'unicode'}},
+            type = 'tree', unique = true
+        })
+        box.space.client:create_index('idx_client_esia_id', {
+            parts = {{field = 'esia_id', collation = 'unicode'}},
+            type = 'tree', unique = false
+        })
+
+        box.schema.space.create('client_phones', {
+            engine = engine,
+            foreign_key = {space = 'client',
+                           field = {customer_id = 'customer_id'}}
+        })
+        box.space.client_phones:format({
+            {name = 'phone', type = 'string', is_nullable = false},
+            {name = 'customer_id'},
+        })
+
+        box.space.client_phones:create_index('idx_client_phones_phone', {
+            parts = {{field = 'phone', collation = 'unicode'}},
+            type = 'tree', unique = true
+        })
+
+        box.space.client:insert{'01','esia-01'}
+        box.space.client:insert{'02','esia-02'}
+
+        box.space.client_phones:insert{'9121234','01'}
+        box.space.client_phones:insert{'3222222','02'}
+
+        -- Now truncate is prohibited.
+        t.assert_error_msg_content_equals(
+            "Can't modify space 'client': space is referenced by foreign key",
+            box.space.client.truncate, box.space.client)
+
+        box.space.client_phones:truncate()
+
+        -- Now truncate is allowed.
+        box.space.client:truncate()
+    end, {engine})
+end
diff --git a/test/sql/delete.result b/test/sql/delete.result
index 3c2ed1e2e90027d1a983509a6a385d7deb94147a..3f3b8e5ef08a43ba87ea4c2413e870b5d6b0ca1d 100644
--- a/test/sql/delete.result
+++ b/test/sql/delete.result
@@ -141,6 +141,10 @@ box.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")
 ---
 - row_count: 1
 ...
+box.execute("INSERT INTO t2 VALUES(1);")
+---
+- row_count: 1
+...
 box.execute("TRUNCATE TABLE t1;")
 ---
 - null
diff --git a/test/sql/delete.test.lua b/test/sql/delete.test.lua
index 82cf3322320b93abde9ce7d8eedcc204af66ed40..8eab0b0aead93a05306e1ed090b4354836d505b7 100644
--- a/test/sql/delete.test.lua
+++ b/test/sql/delete.test.lua
@@ -60,6 +60,7 @@ box.execute("TRUNCATE TABLE v1;")
 
 -- Can't truncate table with FK.
 box.execute("CREATE TABLE t2(x INT PRIMARY KEY REFERENCES t1(id));")
+box.execute("INSERT INTO t2 VALUES(1);")
 box.execute("TRUNCATE TABLE t1;")
 
 -- Table triggers should be ignored.