{"record":{"id":"079fe82bd49083a5","repo":"gastownhall/beads","slug":"legacy-sqlite-s-is-d-characters-current-varchar","errorCode":null,"errorMessage":"legacy SQLite %s is %d characters (current VARCHAR(%d) maximum)","messagePattern":"legacy SQLite (.+?) is (.+?) characters \\(current VARCHAR\\((.+?)\\) maximum\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":744,"sourceCode":"\t\t{\"issue event_kind\", issue.EventKind, currentShortVarcharRunes},\n\t\t{\"issue actor\", issue.Actor, types.MaxFieldLen},\n\t\t{\"issue target\", issue.Target, types.MaxFieldLen},\n\t\t{\"issue work_type\", string(issue.WorkType), currentShortVarcharRunes},\n\t\t{\"issue source_system\", issue.SourceSystem, types.MaxFieldLen},\n\t}\n\tif issue.ExternalRef != nil {\n\t\tfields = append(fields, currentVarchar{\"issue external_ref\", *issue.ExternalRef, types.MaxFieldLen})\n\t}\n\tif issue.CompactedAtCommit != nil {\n\t\tfields = append(fields, currentVarchar{\"issue compacted_at_commit\", *issue.CompactedAtCommit, currentCommitVarcharRunes})\n\t}\n\treturn checkCurrentVarchars(fields...)\n}\n\nfunc checkCurrentVarchars(fields ...currentVarchar) error {\n\tfor _, field := range fields {\n\t\tif n := utf8.RuneCountInString(field.value); n > field.maxRunes {\n\t\t\treturn fmt.Errorf(\"legacy SQLite %s is %d characters (current VARCHAR(%d) maximum)\", field.name, n, field.maxRunes)\n\t\t}\n\t}\n\treturn nil\n}\n\ntype currentInt struct {\n\tname  string\n\tvalue sql.NullInt64\n}\n\nfunc checkCurrentInts(fields ...currentInt) error {\n\tfor _, field := range fields {\n\t\tif field.value.Valid && (field.value.Int64 < math.MinInt32 || field.value.Int64 > math.MaxInt32) {\n\t\t\treturn fmt.Errorf(\"legacy SQLite %s is %d (current INT range %d..%d)\", field.name, field.value.Int64, math.MinInt32, math.MaxInt32)\n\t\t}\n\t}\n\treturn nil\n}","sourceCodeStart":726,"sourceCodeEnd":762,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L726-L762","documentation":"During legacy SQLite migration, a text field exceeds the current Dolt-backed VARCHAR column width (measured in runes, not bytes). The reader pre-validates every issue, label, dependency, and comment field against the current schema limits so the import fails loudly instead of being silently truncated or rejected by the target database. The error names the offending field, its rune count, and the allowed maximum.","triggerScenarios":"Importing a legacy SQLite beads database whose issue title, id, assignee, external_ref, label, dependency type/created_by, or similar VARCHAR column holds more runes than the current limit (e.g. types.MaxFieldLen for ids/labels, currentTitleVarcharRunes for titles, currentShortVarcharRunes for status/type). Raised from checkCurrentVarchars via validateIssueVarchars, loadLabels, appendLegacyDependencyRow, and loadComments.","commonSituations":"Older bd versions stored longer titles or free-form external_refs before the current schema tightened VARCHAR widths; hand-edited databases with oversized labels; issues imported from other trackers with long IDs or actor names; multi-byte text where byte-count checks passed historically but rune counts now exceed the limit.","solutions":["Identify the field named in the error and shorten its value in the legacy SQLite database (e.g. UPDATE issues SET title = ... WHERE id = ...) before re-running the migration.","Check the bd version that wrote the legacy DB; upgrade through an intermediate release that migrates oversized fields, if one exists.","If the field is a label or external_ref, trim or split it to fit types.MaxFieldLen runes.","For bulk fixes, script a pre-pass over the SQLite DB that flags every column whose utf8 rune length exceeds the reported maximum.","If you believe the limit is wrong rather than the data, open an issue against beads rather than patching the reader locally."],"exampleFix":"// before: legacy row with a 300-rune title\n// after: shorten before migration\nUPDATE issues SET title = substr(title, 1, 200) WHERE length(title) > 200;","handlingStrategy":"validation","validationCode":"import \"unicode/utf8\"\n\nfunc fieldFitsCurrentVarchar(name, value string, maxRunes int) error {\n    if n := utf8.RuneCountInString(value); n > maxRunes {\n        return fmt.Errorf(\"%s is %d characters (max %d)\", name, n, maxRunes)\n    }\n    return nil\n}\n// run over title, labels, ids, external_ref before migration","typeGuard":"func withinVarcharLimit(s string, maxRunes int) bool {\n    return utf8.RuneCountInString(s) <= maxRunes\n}","tryCatchPattern":null,"preventionTips":["Pre-scan the legacy SQLite DB and truncate/trim fields exceeding current VARCHAR rune limits before migrating.","Measure lengths in runes, not bytes, when auditing legacy text columns.","Keep field lengths enforced at write time in the producing application so legacy data is born within limits.","Log oversized fields during a dry-run pass so fixes can be batched."],"tags":["migration","sqlite","schema-validation","varchar-limit"],"backgroundTag":"varchar-length-exceeded","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}