MY.L6.INNODB_ROW_FORMAT_NOT_DYNAMIC — the 767-byte ceiling that aborts a deploy halfway
- Category: safety
- Level: 6
- Confidence: deterministic
- Downtime class: none
- Stability: stable
- Suites: audit
- Applies to: MySQL 8.4
- Not transferable to MariaDB: this page describes MySQL 8.4 behavior. MariaDB answers the same driver and does not share these semantics, so SQLens refuses it outright rather than reasoning about it — see drivers/unsupported.
Laravel's schema builder never emits an explicit ROW_FORMAT, so every table it creates takes
the server default.
Under COMPACT or REDUNDANT that default carries the 767-byte index key prefix ceiling
instead of 3072. At utf8mb4's four bytes per character, that is 191 characters — so the first
$table->string('email')->unique();
(VARCHAR(255) × 4 = 1020 bytes) aborts with ERROR 1071 Specified key was too long.
The timing is the damage
It aborts mid-deploy. Some migrations are applied, some are not, on a server every other check called healthy.
Why the usual workaround is worse than the fix
The field answer everyone reaches for is Schema::defaultStringLength(191) in a service provider.
That converts a one-line server misconfiguration into a permanent, application-wide narrowing of
every string column — invisible to whoever reads the migrations later, and carried onto servers that
never had the problem.
Flagged
SET GLOBAL innodb_default_row_format = COMPACT;
Preferred
SET GLOBAL innodb_default_row_format = DYNAMIC;
What this rule does not report
A server correctly handing out DYNAMIC while the catalog still holds tables created years ago under
an older default.
There is nothing in the configuration to fix there. Reporting it would send a reader to a file that
is already correct while never naming the actual work — an ALTER TABLE … ROW_FORMAT=DYNAMIC rebuild
per table. That is a statement about the schema, not the server, and it belongs to a schema rule.
When the server default is wrong, the finding does name the tables already on a legacy format, so you know whether a rebuild is owed on top of the configuration change.
What is read
The variable is GLOBAL-only — there is no session value to read, and none is looked for. The
comparison folds case: MySQL reports dynamic while the manual and every config file write
DYNAMIC, and a strict comparison would flag the exact value the expectation asks for.
Sources
- InnoDB row formats — MySQL 8.4
- InnoDB limits — MySQL 8.4
- InnoDB startup options and system variables — MySQL 8.4