{"record":{"id":"a981149c8a3851f0","repo":"gastownhall/beads","slug":"timestamp-q-w","errorCode":null,"errorMessage":"timestamp %q: %w","messagePattern":"timestamp %q: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":783,"sourceCode":"\tfor _, v := range values {\n\t\tif v.Valid && v.String != \"\" {\n\t\t\treturn true\n\t\t}\n\t}\n\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","sourceCodeStart":765,"sourceCodeEnd":801,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L765-L801","documentation":"parseTime successfully parsed a timestamp string with one of the supported layouts (RFC3339Nano or SQLite-style datetime formats), but canonicalizing it to the current DATETIME representation failed. The original string is included in the message and the underlying cause (from canonicalCurrentDatetime) is wrapped with %w, so this error typically indicates a timestamp that parses but is unrepresentable (see the wrapped year-range error).","triggerScenarios":"A dependency row's created_at or a comment/issue timestamp parses under one of the three accepted layouts, but t.UTC().Round(time.Second) yields a year outside 0..9999 (or another canonicalization failure). Raised from parseTime for callers parseOptionalTime, appendLegacyDependencyRow, and loadComments.","commonSituations":"Timestamps with extreme years (e.g. year 10000+) written by buggy tools; hand-edited SQLite rows with nonsense datetimes; dates far in the future like 9999-12-31T23:59:60 that round up past the representable range.","solutions":["Read the wrapped cause in the message chain to see why canonicalization failed (usually year out of range).","Fix the offending timestamp in the legacy SQLite DB to a realistic date before migrating (e.g. UPDATE dependencies SET created_at = ...).","Ensure timestamps are written in RFC3339 or '2006-01-02 15:04:05' format with years 0001-9999.","If seconds rounding pushes the value out of range (e.g. 9999-12-31T23:59:59.9), nudge the value back slightly."],"exampleFix":"// before: created_at = '10000-01-01 00:00:00'\nUPDATE dependencies SET created_at = '2024-01-01 00:00:00' WHERE created_at = '10000-01-01 00:00:00';\n// after: canonicalizable timestamp","handlingStrategy":"validation","validationCode":"func timestampCanonicalizable(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 t, err := time.Parse(layout, s); err == nil {\n            y := t.UTC().Round(time.Second).Year()\n            return y >= 0 && y <= 9999\n        }\n    }\n    return false\n}","typeGuard":"func isStorableDatetime(t time.Time) bool {\n    y := t.UTC().Round(time.Second).Year()\n    return y >= 0 && y <= 9999\n}","tryCatchPattern":"// Go: wrap the migration call and inspect the wrapped cause\nif err := migrateLegacy(db); err != nil {\n    var inner error\n    if errors.As(err, &inner) || strings.Contains(err.Error(), \"timestamp\") {\n        log.Fatalf(\"fix timestamp in legacy DB: %v\", err)\n    }\n    return err\n}","preventionTips":["Store all legacy timestamps in RFC3339 with years 0001-9999.","Pre-parse every datetime column with the accepted layouts before running migration.","Avoid far-future sentinel dates near the year-9999 boundary."],"tags":["migration","sqlite","timestamp","datetime"],"backgroundTag":"invalid-timestamp","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}