{"record":{"id":"48b158bdc2078cc5","repo":"gastownhall/beads","slug":"invalid-timestamp-q","errorCode":null,"errorMessage":"invalid timestamp %q","messagePattern":"invalid timestamp %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":788,"sourceCode":"\treturn false\n}\nfunc nullString(v sql.NullString) string {\n\tif v.Valid {\n\t\treturn v.String\n\t}\n\treturn \"\"\n}\nfunc parseTime(s string) (time.Time, error) {\n\tfor _, layout := range []string{time.RFC3339Nano, \"2006-01-02 15:04:05.999999999-07:00\", \"2006-01-02 15:04:05.999999999\"} {\n\t\tif t, e := time.Parse(layout, s); e == nil {\n\t\t\tcanonical, err := canonicalCurrentDatetime(t)\n\t\t\tif err != nil {\n\t\t\t\treturn time.Time{}, fmt.Errorf(\"timestamp %q: %w\", s, err)\n\t\t\t}\n\t\t\treturn canonical, nil\n\t\t}\n\t}\n\treturn time.Time{}, fmt.Errorf(\"invalid timestamp %q\", s)\n}\n\nfunc canonicalCurrentDatetime(t time.Time) (time.Time, error) {\n\tcanonical := t.UTC().Round(time.Second)\n\tif year := canonical.Year(); year < 0 || year > 9999 {\n\t\treturn time.Time{}, fmt.Errorf(\"timestamp rounds outside current DATETIME range\")\n\t}\n\treturn canonical, nil\n}\n\nfunc parseOptionalTime(name string, raw sql.NullString) (*time.Time, error) {\n\tif !raw.Valid {\n\t\treturn nil, nil\n\t}\n\tparsed, err := parseTime(raw.String)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: %w\", name, err)\n\t}","sourceCodeStart":770,"sourceCodeEnd":806,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L770-L806","documentation":"parseTime could not parse the timestamp string with any of its three accepted layouts: RFC3339Nano, '2006-01-02 15:04:05.999999999-07:00', or '2006-01-02 15:04:05.999999999'. The reader refuses to import rows with unparseable datetimes so the current database never receives malformed DATETIME values.","triggerScenarios":"A legacy SQLite row's created_at/updated_at/closed_at column contains text in an unsupported format — e.g. Unix epoch integers, 'YYYY/MM/DD' slashes, local formats like '01/02/2020 3pm', or empty-but-non-null strings — passed via parseOptionalTime, appendLegacyDependencyRow, or loadComments.","commonSituations":"Very old bd versions writing epoch integers instead of text timestamps; third-party SQLite editors inserting locale-formatted dates; CAST artifacts producing formats with unexpected timezone suffixes; test databases seeded with fake date strings.","solutions":["Locate the row whose timestamp matches %q in the message and inspect the raw column value.","Rewrite the value to RFC3339 (e.g. '2024-05-01T12:00:00Z') or 'YYYY-MM-DD HH:MM:SS' in the legacy SQLite DB.","If the value is a Unix epoch number, convert it: UPDATE t SET ts = strftime('%Y-%m-%d %H:%M:%S', ts, 'unixepoch').","Scan the whole legacy DB for non-conforming timestamps before migrating (regex pre-pass over datetime columns)."],"exampleFix":"// before: created_at = '1714560000' (unix epoch)\nUPDATE dependencies SET created_at = strftime('%Y-%m-%d %H:%M:%S', created_at, 'unixepoch') WHERE created_at GLOB '[0-9]*' AND created_at NOT GLOB '*-*';\n// after: parseable datetime text","handlingStrategy":"validation","validationCode":"func parseableTimestamp(s string) bool {\n    for _, layout := range []string{time.RFC3339Nano, \"2006-01-02 15:04:05.999999999-07:00\", \"2006-01-02 15:04:05.999999999\"} {\n        if _, err := time.Parse(layout, s); err == nil {\n            return true\n        }\n    }\n    return false\n}\n// scan created_at/updated_at/closed_at columns with this before migrating","typeGuard":null,"tryCatchPattern":"if err := migrateLegacy(db); err != nil {\n    if strings.Contains(err.Error(), \"invalid timestamp\") {\n        // extract %q value from message, fix that row, retry\n        return fmt.Errorf(\"normalize timestamps in legacy DB and retry: %w\", err)\n    }\n    return err\n}","preventionTips":["Normalize all datetime columns to 'YYYY-MM-DD HH:MM:SS' or RFC3339 before migration.","Convert Unix-epoch integer timestamps with SQLite's strftime(..., 'unixepoch').","Never hand-edit datetime columns with locale-specific formats.","Run a dry-run migration on a copy to catch all bad formats in one pass."],"tags":["migration","sqlite","timestamp","datetime-format"],"backgroundTag":"invalid-timestamp","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}