{"record":{"id":"d8bb0cba7728b8f6","repo":"gastownhall/beads","slug":"parsing-last-pull-timestamp-q-w","errorCode":null,"errorMessage":"parsing last_pull timestamp %q: %w","messagePattern":"parsing last_pull timestamp %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/linear/staleness.go","lineNumber":47,"sourceCode":"func ReadLastPullTimestamp(beadsDir string) (time.Time, error) {\n\tif beadsDir == \"\" {\n\t\treturn time.Time{}, fmt.Errorf(\"beadsDir must not be empty\")\n\t}\n\tpath := filepath.Join(beadsDir, lastPullFileName)\n\tdata, err := os.ReadFile(path) // #nosec G304 -- path is constrained to the beads directory.\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn time.Time{}, nil\n\t\t}\n\t\treturn time.Time{}, fmt.Errorf(\"reading last_pull: %w\", err)\n\t}\n\tts := strings.TrimSpace(string(data))\n\tif ts == \"\" {\n\t\treturn time.Time{}, nil\n\t}\n\tt, err := time.Parse(time.RFC3339, ts)\n\tif err != nil {\n\t\treturn time.Time{}, fmt.Errorf(\"parsing last_pull timestamp %q: %w\", ts, err)\n\t}\n\treturn t, nil\n}\n\n// IsPullStale returns true if the last pull is older than the given threshold,\n// or if no pull has ever been recorded.\nfunc IsPullStale(beadsDir string, threshold time.Duration) bool {\n\tlastPull, err := ReadLastPullTimestamp(beadsDir)\n\tif err != nil || lastPull.IsZero() {\n\t\treturn true\n\t}\n\treturn time.Since(lastPull) > threshold\n}\n\n// StalenessInfo holds computed staleness details for display purposes.\ntype StalenessInfo struct {\n\tLastPull    time.Time\n\tAge         time.Duration","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/staleness.go#L29-L65","documentation":"After reading .beads/last_pull, ReadLastPullTimestamp parses the trimmed contents as RFC 3339 (the format WriteLastPullTimestamp writes). If the contents are non-empty but not valid RFC 3339, it returns this wrapped parse error including the offending value %q. This indicates the timestamp file was corrupted or written by an incompatible tool.","triggerScenarios":"time.Parse(time.RFC3339, ts) fails on the file contents — e.g. the file holds a Unix epoch number, a locale-formatted date, partial/truncated text, or binary junk instead of an ISO 8601 string like 2026-08-30T12:00:00Z.","commonSituations":"An older beads version wrote a different timestamp format; the file was truncated by a crash or full disk; a user or script edited last_pull manually; another tool claimed the same file path.","solutions":["Delete the corrupt last_pull file — staleness logic treats 'no file' as 'never pulled' and WriteLastPullTimestamp will rewrite it after the next pull","Check the beads version that wrote the file and upgrade/downgrade so formats match","Manually rewrite the file with a valid RFC 3339 timestamp if you must preserve it","Add a startup sanity check that rewrites last_pull when parsing fails"],"exampleFix":"// before: trust file blindly\nt, err := ReadLastPullTimestamp(beadsDir)\n// after: self-heal on parse error\nt, err := ReadLastPullTimestamp(beadsDir)\nif err != nil {\n    os.Remove(filepath.Join(beadsDir, \"last_pull\"))\n    t = time.Time{}\n}","handlingStrategy":"fallback","validationCode":"// validate before relying on the value\nif data, err := os.ReadFile(filepath.Join(beadsDir, \"last_pull\")); err == nil {\n    if _, perr := time.Parse(time.RFC3339, strings.TrimSpace(string(data))); perr != nil {\n        os.Remove(filepath.Join(beadsDir, \"last_pull\"))\n    }\n}","typeGuard":"null","tryCatchPattern":"t, err := ReadLastPullTimestamp(beadsDir)\nif err != nil {\n    log.Printf(\"corrupt last_pull, resetting: %v\", err)\n    os.Remove(filepath.Join(beadsDir, \"last_pull\"))\n    t = time.Time{}\n}","preventionTips":["Always write last_pull via WriteLastPullTimestamp so the format stays RFC 3339","Never hand-edit or redirect other output into last_pull","Self-heal by deleting the file on parse failure; treat as never pulled"],"tags":["parsing","filesystem","data-corruption"],"backgroundTag":"timestamp-parse-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}