Skip to main content

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