{"record":{"id":"308e62883011ed3c","repo":"gastownhall/beads","slug":"legacy-sqlite-issue-payload-is-d-bytes-current-t","errorCode":null,"errorMessage":"legacy SQLite issue payload is %d bytes (current TEXT maximum %d)","messagePattern":"legacy SQLite issue payload is (.+?) bytes \\(current TEXT maximum (.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":679,"sourceCode":"\t\t\t\treturn fmt.Errorf(\"lone high UTF-16 surrogate escape\")\n\t\t\t}\n\t\t\tlow, err := strconv.ParseUint(raw[next+2:next+6], 16, 16)\n\t\t\tif err != nil || low < 0xdc00 || low > 0xdfff {\n\t\t\t\treturn fmt.Errorf(\"lone high UTF-16 surrogate escape\")\n\t\t\t}\n\t\t\ti = next + 5\n\t\tcase code >= 0xdc00 && code <= 0xdfff:\n\t\t\treturn fmt.Errorf(\"lone low UTF-16 surrogate escape\")\n\t\tdefault:\n\t\t\ti += 5\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc validateCurrentTextBytes(issue *types.Issue) error {\n\tif len(issue.Payload) > currentTextBytes {\n\t\treturn fmt.Errorf(\"legacy SQLite issue payload is %d bytes (current TEXT maximum %d)\", len(issue.Payload), currentTextBytes)\n\t}\n\twaiters := issueops.FormatJSONStringArray(issue.Waiters)\n\tif len(waiters) > currentTextBytes {\n\t\treturn fmt.Errorf(\"legacy SQLite issue waiters serialize to %d bytes (current TEXT maximum %d)\", len(waiters), currentTextBytes)\n\t}\n\treturn nil\n}\n\nfunc decodeWaiters(raw string) ([]string, error) {\n\tvar decoded any\n\tif err := json.Unmarshal([]byte(raw), &decoded); err != nil {\n\t\treturn nil, err\n\t}\n\tvalues, ok := decoded.([]any)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"must be an array of strings\")\n\t}\n\twaiters := make([]string, len(values))","sourceCodeStart":661,"sourceCodeEnd":697,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L661-L697","documentation":"validateCurrentTextBytes enforces the current storage TEXT column limit (currentTextBytes) on migrated issues. If the issue's serialized Payload exceeds that limit, the migration refuses the row rather than risk a database write failure downstream.","triggerScenarios":"len(issue.Payload) > currentTextBytes for an issue read from the legacy SQLite database during validate().","commonSituations":"Legacy rows containing very large issue bodies accumulated over years, attachments embedded as text, or a lowered TEXT limit in the current schema.","solutions":["Trim or split the oversized issue payload before migrating.","Increase the target TEXT column limit if the storage backend allows it.","Archive the large legacy issue externally and migrate a truncated placeholder."],"exampleFix":"// before: payload of 2,000,000 bytes migrated as-is\n// after\npayload := truncateToBytes(issue.Payload, currentTextBytes)","handlingStrategy":"validation","validationCode":"if len(issue.Payload) > currentTextBytes { return fmt.Errorf(\"payload too large: %d > %d\", len(issue.Payload), currentTextBytes) }","typeGuard":null,"tryCatchPattern":"if err := validateCurrentTextBytes(issue); err != nil { log.Warn(\"skipping oversized issue\", issue.ID); continue }","preventionTips":["Monitor legacy payload sizes before migration","Set a pre-migration size budget and split oversized issues","Keep TEXT column limits documented and checked in CI data tests"],"tags":["migration","sqlite","size-limit","payload"],"backgroundTag":"text-column-overflow","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}