Found while building the field-type agreement pin for #11565. Filed unassigned; deliberately not fixed there — that card is a MySQL diagnostic and this is an emission-rule defect on every dialect.
What
createColumn's text-family case in packages/drivers/driver-sql/src/sql-driver.ts lists text / textarea / html / markdown. richtext is not in it. It is also not in JSON_COLUMN_TYPES, so it falls through to the catch-all and is emitted as table.string(name) — knex's varchar(255).
The spec groups all three together as "Rich Content" (field.zod.ts: 'markdown', 'html', 'richtext') and puts all of them in STRING_VALUE_TYPES. Two of the three get an unbounded TEXT column. The third gets 255 characters.
The same fall-through catches three more STRING_VALUE_TYPES members whose values are routinely long: code (a code-editor field), signature and qrcode (both plausibly data URIs).
Evidence
Measured, not inferred — this is what the FieldType.options agreement pin added in #11565 compares against a really-created table, column by column:
richtext STRING_VALUE: true STRUCTURED_JSON: false -> varchar(255)
code STRING_VALUE: true STRUCTURED_JSON: false -> varchar(255)
signature STRING_VALUE: true STRUCTURED_JSON: false -> varchar(255)
qrcode STRING_VALUE: true STRUCTURED_JSON: false -> varchar(255)
markdown STRING_VALUE: true STRUCTURED_JSON: false -> text
html STRING_VALUE: true STRUCTURED_JSON: false -> text
Why it matters
varchar(255) is not a soft cap on either enforcing dialect. This is the exact write-side failure #11431 documents for the string family: a legal value is refused by MySQL with ER_DATA_TOO_LONG under STRICT_TRANS_TABLES, and by Postgres with 22001 value too long for type character varying(255). A rich-text body over 255 characters is an ordinary authored value, and nothing in the metadata declares that bound — the author declared richtext, not a length.
It is also a self-inflicted drift report: schema-drift.ts treats varchar(field.maxLength) as the expected physical shape of a bounded field, and these fields declare no bound.
Not prescribing a remedy
The obvious reading is that richtext belongs in the text-family case beside markdown and html (with the same #11374 keyed-and-bounded rule). Whether code / signature / qrcode belong there too, or whether the whole switch should be driven off the spec's STRING_VALUE_TYPES value class rather than a hand-maintained case list, is the design question — the hand-maintained list is what let one member of a three-member spec group diverge silently.
Scope note for whoever picks this up
Lands in the same createColumn switch as #11431 (string family / maxLength) and #11374 (keyed text). Same file, adjacent branches — worth serialising against those rather than running in parallel. Not blocked by either: this branch is independent of both.
The agreement pin from #11565 (sql-driver-11565-row-byte-budget.test.ts, "agrees with createColumn about every FieldType the spec declares") will need its expectation updated in the same PR — it currently pins the present behaviour, including this one.
Found while building the field-type agreement pin for #11565. Filed unassigned; deliberately not fixed there — that card is a MySQL diagnostic and this is an emission-rule defect on every dialect.
What
createColumn's text-family case inpackages/drivers/driver-sql/src/sql-driver.tsliststext/textarea/html/markdown.richtextis not in it. It is also not inJSON_COLUMN_TYPES, so it falls through to the catch-all and is emitted astable.string(name)— knex'svarchar(255).The spec groups all three together as "Rich Content" (
field.zod.ts:'markdown', 'html', 'richtext') and puts all of them inSTRING_VALUE_TYPES. Two of the three get an unbounded TEXT column. The third gets 255 characters.The same fall-through catches three more
STRING_VALUE_TYPESmembers whose values are routinely long:code(a code-editor field),signatureandqrcode(both plausibly data URIs).Evidence
Measured, not inferred — this is what the
FieldType.optionsagreement pin added in #11565 compares against a really-created table, column by column:Why it matters
varchar(255)is not a soft cap on either enforcing dialect. This is the exact write-side failure #11431 documents for the string family: a legal value is refused by MySQL withER_DATA_TOO_LONGunderSTRICT_TRANS_TABLES, and by Postgres with22001 value too long for type character varying(255). A rich-text body over 255 characters is an ordinary authored value, and nothing in the metadata declares that bound — the author declaredrichtext, not a length.It is also a self-inflicted drift report:
schema-drift.tstreatsvarchar(field.maxLength)as the expected physical shape of a bounded field, and these fields declare no bound.Not prescribing a remedy
The obvious reading is that
richtextbelongs in the text-family case besidemarkdownandhtml(with the same #11374 keyed-and-bounded rule). Whethercode/signature/qrcodebelong there too, or whether the whole switch should be driven off the spec'sSTRING_VALUE_TYPESvalue class rather than a hand-maintained case list, is the design question — the hand-maintained list is what let one member of a three-member spec group diverge silently.Scope note for whoever picks this up
Lands in the same
createColumnswitch as #11431 (string family /maxLength) and #11374 (keyed text). Same file, adjacent branches — worth serialising against those rather than running in parallel. Not blocked by either: this branch is independent of both.The agreement pin from #11565 (
sql-driver-11565-row-byte-budget.test.ts, "agrees with createColumn about every FieldType the spec declares") will need its expectation updated in the same PR — it currently pins the present behaviour, including this one.