{"record":{"id":"786526d1193b05a3","repo":"gastownhall/beads","slug":"legacy-sqlite-issue-s-updated-at-w","errorCode":null,"errorMessage":"legacy SQLite issue %s updated_at: %w","messagePattern":"legacy SQLite issue (.+?) updated_at: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":544,"sourceCode":"\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 {\n\t\treturn fmt.Errorf(\"legacy SQLite issue %s created_at: %w\", issue.ID, err)\n\t}\n\tif issue.UpdatedAt, err = canonicalCurrentDatetime(issue.UpdatedAt); err != nil {\n\t\treturn fmt.Errorf(\"legacy SQLite issue %s updated_at: %w\", issue.ID, err)\n\t}\n\treturn nil\n}\n\n// checkRequiredScalars enforces the non-empty ID, non-tombstone status, and\n// present created_at/updated_at invariants every legacy issue must satisfy.\nfunc checkRequiredScalars(issue *types.Issue) error {\n\tif issue.ID == \"\" {\n\t\treturn fmt.Errorf(\"legacy SQLite issue has empty ID\")\n\t}\n\tif issue.Status == \"tombstone\" {\n\t\treturn fmt.Errorf(\"legacy SQLite issue %s is a tombstone\", issue.ID)\n\t}\n\tif issue.CreatedAt.IsZero() || issue.UpdatedAt.IsZero() {\n\t\treturn fmt.Errorf(\"legacy SQLite issue %s has invalid created_at or updated_at\", issue.ID)\n\t}\n\treturn nil\n}","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L526-L562","documentation":"Companion to the created_at normalization error: applyCanonicalTimestamps failed to normalize the issue's updated_at value to the canonical current-schema datetime. Migration will not import an issue whose last-updated timestamp cannot be parsed, since updated_at is required by the current schema.","triggerScenarios":"Legacy row's updated_at is empty, NULL-derived, or in a format canonicalCurrentDatetime cannot parse (non-ISO string, integer epoch in unexpected units, etc.).","commonSituations":"Third-party edits to the legacy DB, very old schema versions with different timestamp encoding, or partial writes from a crashed process.","solutions":["Inspect the raw value: sqlite3 legacy.db \"SELECT id, updated_at FROM issues WHERE id='<id>'\"","Rewrite updated_at as a valid ISO-8601 UTC timestamp, e.g. '2024-06-01T12:00:00Z'","Use created_at as a fallback value if the true update time is unrecoverable","Re-run the migration"],"exampleFix":"// before\nupdated_at = '1717243200000'\n// after\nupdated_at = '2024-06-01T12:00:00Z'","handlingStrategy":"validation","validationCode":"from datetime import datetime\ndef ts_ok(v):\n    if not v:\n        return False\n    try:\n        datetime.fromisoformat(v.replace('Z', '+00:00'))\n        return True\n    except ValueError:\n        return False\n# bad = [r['id'] for r in rows if not ts_ok(r['updated_at'])]","typeGuard":"def is_canonical_datetime(v) -> bool:\n    if not isinstance(v, str) or not v:\n        return False\n    try:\n        datetime.fromisoformat(v.replace('Z', '+00:00'))\n        return True\n    except ValueError:\n        return False","tryCatchPattern":null,"preventionTips":["Audit both created_at and updated_at columns in one pass","Backfill missing/odd timestamps before migration","Keep timestamps in ISO-8601 UTC everywhere"],"tags":["migration","sqlite","timestamps"],"backgroundTag":"invalid-timestamp-format","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}