{"record":{"id":"204c8e74237f4d1b","repo":"gastownhall/beads","slug":"legacy-sqlite-issue-s-has-invalid-metadata-json","errorCode":null,"errorMessage":"legacy SQLite issue %s has invalid metadata JSON","messagePattern":"legacy SQLite issue (.+?) has invalid metadata JSON","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":510,"sourceCode":"\t}\n\tif issue.CompactedAt, err = parseOptionalTime(\"compacted_at\", x.compactedAt); err != nil {\n\t\treturn fmt.Errorf(\"legacy SQLite issue %s: %w\", issue.ID, err)\n\t}\n\tif issue.DueAt, err = parseOptionalTime(\"due_at\", x.dueAt); err != nil {\n\t\treturn fmt.Errorf(\"legacy SQLite issue %s: %w\", issue.ID, err)\n\t}\n\tif issue.DeferUntil, err = parseOptionalTime(\"defer_until\", x.deferUntil); err != nil {\n\t\treturn fmt.Errorf(\"legacy SQLite issue %s: %w\", issue.ID, err)\n\t}\n\treturn nil\n}\n\n// applyMetadataAndWaiters validates the legacy metadata and waiters JSON blobs\n// (well-formed and free of unpaired surrogates) and assigns them to issue.\n// metadata is stored verbatim unless it is the empty object; waiters is decoded.\nfunc (x legacyExtras) applyMetadataAndWaiters(issue *types.Issue) error {\n\tif x.metadata.Valid && x.metadata.String != \"\" && !json.Valid([]byte(x.metadata.String)) {\n\t\treturn fmt.Errorf(\"legacy SQLite issue %s has invalid metadata JSON\", issue.ID)\n\t}\n\tif x.metadata.Valid && x.metadata.String != \"\" {\n\t\tif err := checkJSONSurrogates(x.metadata.String); err != nil {\n\t\t\treturn fmt.Errorf(\"legacy SQLite issue %s metadata: %w\", issue.ID, err)\n\t\t}\n\t}\n\tif x.metadata.Valid && x.metadata.String != \"\" && x.metadata.String != \"{}\" {\n\t\tissue.Metadata = []byte(x.metadata.String)\n\t}\n\tif x.waiters.Valid && x.waiters.String != \"\" {\n\t\tif !json.Valid([]byte(x.waiters.String)) {\n\t\t\treturn fmt.Errorf(\"issue %s waiters: invalid JSON\", issue.ID)\n\t\t}\n\t\tif err := checkJSONSurrogates(x.waiters.String); err != nil {\n\t\t\treturn fmt.Errorf(\"issue %s waiters: %w\", issue.ID, err)\n\t\t}\n\t\twaiters, err := decodeWaiters(x.waiters.String)\n\t\tif err != nil {","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L492-L528","documentation":"applyMetadataAndWaiters checks that a legacy issue's non-empty metadata column is well-formed JSON via json.Valid; if not, it reports 'legacy SQLite issue <id> has invalid metadata JSON'. Valid JSON is required because metadata is stored verbatim into the new issue record.","triggerScenarios":"Migrating a legacy DB where an issues.metadata column holds text that is not parseable JSON (truncated blob, plain string, concatenated fragments).","commonSituations":"Truncated TEXT columns from interrupted writes; manual edits appending prose to the JSON; encoding bugs in old exporter tools; NULL-terminator or BOM artifacts breaking the parser.","solutions":["Inspect the metadata column for the reported issue ID and repair it into valid JSON (or set it to NULL/'{}'), then re-run migration.","Re-serialize the intended key/value pairs into a valid JSON object before migrating.","Clear the corrupted metadata (NULL) if it can be reconstructed after migration."],"exampleFix":"-- before\nUPDATE issues SET metadata='{\"key\": ' WHERE id='bd-3';\n-- after\nUPDATE issues SET metadata='{\"key\": \"value\"}' WHERE id='bd-3';","handlingStrategy":"validation","validationCode":"rows, _ := db.Query(`SELECT id FROM issues WHERE metadata IS NOT NULL AND metadata != '' `)\nfor rows.Next() {\n  var id, meta string\n  rows.Scan(&id, &meta)\n  if !json.Valid([]byte(meta)) { /* repair before migrate */ }\n}","typeGuard":null,"tryCatchPattern":"if !json.Valid([]byte(metadata)) {\n  // re-serialize or NULL the metadata column before migrating\n}","preventionTips":["Never append/edit metadata TEXT columns by hand.","Check for truncated writes (disk full, killed process) after legacy DB incidents.","Run a json.Valid audit over the metadata column before migrating.","Serialize metadata programmatically, never via string concatenation."],"tags":["sqlite","migration","json","data-validation"],"backgroundTag":"invalid-json-metadata","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}