Found while measuring #12978.
explainUnkeyableTextColumn (packages/drivers/driver-sql/src/sql-driver.ts) renders every ER_BLOB_KEY_WITHOUT_LENGTH / ER_TOO_LONG_KEY index refusal as: the column is TEXT "because the field declares no maxLength (or one wider than 768 characters ...). Declare maxLength on the field(s) so the column is emitted as varchar(n) and can be keyed (#11374)."
That diagnosis is correct at CREATE time, but the same message fires on the upgrade path where it is false in both halves: on a pre-existing table the additive sync never rewrites a column, so after a bound IS declared (e.g. #12978's fix for the five sys_notification_* objects) the field declares a perfectly usable maxLength while the physical column is stale TEXT. Every boot then re-attempts the missing plain indexes, the refusal re-fires, and the message tells the operator to do the thing that has already been done — while the actual remedy on that path (hand-convert the column to varchar(n), backup first, restating NOT NULL/DEFAULT on MySQL MODIFY, then let the next boot create the index) goes unnamed. The #12121 drift message names exactly that manual route for the mirror case (unbounded field over stale varchar), so the wording exists in-tree; this seam just never learned it.
The function already introspects columnInfo() on the failure path to name the offending TEXT columns; it has the field metadata available at its call sites (syncDeclaredIndexes receives the declared indexes and the driver holds managedObjectFields). Branching the message on "field declares a usable bound but the physical column is TEXT" would make the refusal name the real remedy instead of a no-op.
Consequence today: an operator upgrading a MySQL deployment into a release that bounds previously-unbounded keyed columns gets a per-boot error whose stated fix is already applied — the "looks like the fix is broken" shape.
Generated by Claude Code · session_01CPrUz21stTFhJRUirdc4yw · found on #12978
Found while measuring #12978.
explainUnkeyableTextColumn(packages/drivers/driver-sql/src/sql-driver.ts) renders everyER_BLOB_KEY_WITHOUT_LENGTH/ER_TOO_LONG_KEYindex refusal as: the column is TEXT "because the field declares no maxLength (or one wider than 768 characters ...). Declare maxLength on the field(s) so the column is emitted as varchar(n) and can be keyed (#11374)."That diagnosis is correct at CREATE time, but the same message fires on the upgrade path where it is false in both halves: on a pre-existing table the additive sync never rewrites a column, so after a bound IS declared (e.g. #12978's fix for the five sys_notification_* objects) the field declares a perfectly usable maxLength while the physical column is stale TEXT. Every boot then re-attempts the missing plain indexes, the refusal re-fires, and the message tells the operator to do the thing that has already been done — while the actual remedy on that path (hand-convert the column to varchar(n), backup first, restating NOT NULL/DEFAULT on MySQL MODIFY, then let the next boot create the index) goes unnamed. The #12121 drift message names exactly that manual route for the mirror case (unbounded field over stale varchar), so the wording exists in-tree; this seam just never learned it.
The function already introspects
columnInfo()on the failure path to name the offending TEXT columns; it has the field metadata available at its call sites (syncDeclaredIndexesreceives the declared indexes and the driver holdsmanagedObjectFields). Branching the message on "field declares a usable bound but the physical column is TEXT" would make the refusal name the real remedy instead of a no-op.Consequence today: an operator upgrading a MySQL deployment into a release that bounds previously-unbounded keyed columns gets a per-boot error whose stated fix is already applied — the "looks like the fix is broken" shape.
Generated by Claude Code · session_01CPrUz21stTFhJRUirdc4yw · found on #12978