diff --git a/changelogs/unreleased/gh-10278-vy-tuple-format-lookup-fix.md b/changelogs/unreleased/gh-10278-vy-tuple-format-lookup-fix.md
new file mode 100644
index 0000000000000000000000000000000000000000..fd2a6322f895ee5682001a28ad26e6e6724675a2
--- /dev/null
+++ b/changelogs/unreleased/gh-10278-vy-tuple-format-lookup-fix.md
@@ -0,0 +1,4 @@
+## bugfix/vinyl
+
+* Fixed a multi-threading race condition that could cause a writer thread to
+  crash while looking up a tuple format (gh-10278).
diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c
index 54d6ededac260a914d8df3bd2686f2cacb38264a..36dbdf96ad0aa61341485254c8753c911fb7d44a 100644
--- a/src/box/tuple_format.c
+++ b/src/box/tuple_format.c
@@ -44,10 +44,10 @@
 #include <PMurHash.h>
 
 /** Global table of tuple formats */
-struct tuple_format **tuple_formats;
+struct tuple_format *tuple_formats[FORMAT_ID_MAX + 1];
 static intptr_t recycled_format_ids = FORMAT_ID_NIL;
 
-static uint32_t formats_size = 0, formats_capacity = 0;
+static uint32_t formats_size = 0;
 static uint64_t formats_epoch = 0;
 
 /**
@@ -660,27 +660,9 @@ static int
 tuple_format_register(struct tuple_format *format)
 {
 	if (recycled_format_ids != FORMAT_ID_NIL) {
-
 		format->id = (uint16_t) recycled_format_ids;
 		recycled_format_ids = (intptr_t) tuple_formats[recycled_format_ids];
 	} else {
-		if (formats_size == formats_capacity) {
-			uint32_t new_capacity = formats_capacity ?
-						formats_capacity * 2 : 16;
-			struct tuple_format **formats;
-			formats = (struct tuple_format **)
-				realloc(tuple_formats, new_capacity *
-						       sizeof(tuple_formats[0]));
-			if (formats == NULL) {
-				diag_set(OutOfMemory,
-					 sizeof(struct tuple_format), "malloc",
-					 "tuple_formats");
-				return -1;
-			}
-
-			formats_capacity = new_capacity;
-			tuple_formats = formats;
-		}
 		uint32_t formats_size_max = FORMAT_ID_MAX + 1;
 		struct errinj *inj = errinj(ERRINJ_TUPLE_FORMAT_COUNT,
 					    ERRINJ_INT);
@@ -688,7 +670,7 @@ tuple_format_register(struct tuple_format *format)
 			formats_size_max = inj->iparam;
 		if (formats_size >= formats_size_max) {
 			diag_set(ClientError, ER_TUPLE_FORMAT_LIMIT,
-				 (unsigned) formats_capacity);
+				 (unsigned)formats_size_max);
 			return -1;
 		}
 		format->id = formats_size++;
@@ -1290,10 +1272,8 @@ void
 tuple_format_init()
 {
 	tuple_formats_hash = mh_tuple_format_new();
-	tuple_formats = NULL;
 	recycled_format_ids = FORMAT_ID_NIL;
 	formats_size = 0;
-	formats_capacity = 0;
 }
 
 /** Destroy tuple format subsystem and free resourses */
@@ -1314,7 +1294,6 @@ tuple_format_free()
 			free(*format);
 		}
 	}
-	free(tuple_formats);
 	mh_tuple_format_delete(tuple_formats_hash);
 }
 
diff --git a/src/box/tuple_format.h b/src/box/tuple_format.h
index 81eaa4f77a4815efc6b4fb3d626b046c8345d412..b006dc3d60a86e00d5ba01c6823909f027fdae12 100644
--- a/src/box/tuple_format.h
+++ b/src/box/tuple_format.h
@@ -373,7 +373,7 @@ tuple_format_field(struct tuple_format *format, uint32_t fieldno)
 	return tuple_format_field_by_path(format, fieldno, NULL, 0, 0);
 }
 
-extern struct tuple_format **tuple_formats;
+extern struct tuple_format *tuple_formats[];
 
 static inline uint32_t
 tuple_format_id(struct tuple_format *format)
diff --git a/test/wal_off/alter.result b/test/wal_off/alter.result
index 042ca67bcadbae65390741866a28406a253b9e1e..c0aff9502f7f6363a01d890e7b50d1bb07d5cf2f 100644
--- a/test/wal_off/alter.result
+++ b/test/wal_off/alter.result
@@ -24,7 +24,7 @@ for k = 1, box.schema.FORMAT_ID_MAX, 1 do
     table.insert(spaces, s)
 end;
 ---
-- error: 'Tuple format limit reached: 65536'
+- error: 'Tuple format limit reached: 65535'
 ...
 #spaces > 65000;
 ---