{"record":{"id":"68d812e1060e2d15","repo":"gastownhall/beads","slug":"issue-s-waiters-invalid-json","errorCode":null,"errorMessage":"issue %s waiters: invalid JSON","messagePattern":"issue (.+?) waiters: invalid JSON","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":522,"sourceCode":"\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 {\n\t\t\treturn fmt.Errorf(\"issue %s waiters: %w\", issue.ID, err)\n\t\t}\n\t\tissue.Waiters = waiters\n\t}\n\treturn nil\n}\n\n// applyCanonicalTimestamps normalizes the required created_at/updated_at values\n// to the canonical current-schema representation.\nfunc (x legacyExtras) applyCanonicalTimestamps(issue *types.Issue) error {\n\tvar err error\n\tif issue.CreatedAt, err = canonicalCurrentDatetime(issue.CreatedAt); err != nil {","sourceCodeStart":504,"sourceCodeEnd":540,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L504-L540","documentation":"applyMetadataAndWaiters validates the legacy issue's waiters column: it must be valid JSON (this error), free of unpaired surrogates, and decodable by decodeWaiters. A non-empty waiters value that json.Valid rejects aborts migration for that issue with 'issue <id> waiters: invalid JSON'.","triggerScenarios":"Migrating a legacy DB where an issues.waiters column contains non-JSON text (e.g. a comma-separated ID list, truncated array, or empty braces variant) instead of a valid JSON document.","commonSituations":"Old beads versions that stored waiters in a different (non-JSON) serialization; truncated writes; manual edits pasting plain-text waiter lists; data munging by external scripts.","solutions":["Rewrite the waiters column for the reported issue ID as valid JSON in the expected waiters shape (or NULL), then re-run migration.","Check the version that wrote the legacy DB and convert the old waiters serialization to the expected JSON format first.","Clear the corrupted waiters value (NULL) if the waiter relationships can be recreated after migration."],"exampleFix":"-- before\nUPDATE issues SET waiters='bd-1,bd-2' WHERE id='bd-8';\n-- after\nUPDATE issues SET waiters='[\"bd-1\",\"bd-2\"]' WHERE id='bd-8';","handlingStrategy":"validation","validationCode":"rows, _ := db.Query(`SELECT id, waiters FROM issues WHERE waiters IS NOT NULL AND waiters != ''`)\nfor rows.Next() {\n  var id, w string\n  rows.Scan(&id, &w)\n  if !json.Valid([]byte(w)) { /* convert or NULL before migrate */ }\n}","typeGuard":null,"tryCatchPattern":"if !json.Valid([]byte(waiters)) {\n  // convert old serialization to JSON or NULL the column before migrating\n}","preventionTips":["Confirm the legacy DB writer version stores waiters as JSON.","Never paste plain-text ID lists into the waiters column.","Audit waiters with json.Valid before migrating.","Back up and test-migrate a copy of the DB first."],"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"}