gastownhall/beads · warning
parsing last_pull timestamp %q: %w
Error message
parsing last_pull timestamp %q: %w
What it means
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.
Source
Thrown at internal/linear/staleness.go:47
func ReadLastPullTimestamp(beadsDir string) (time.Time, error) {
if beadsDir == "" {
return time.Time{}, fmt.Errorf("beadsDir must not be empty")
}
path := filepath.Join(beadsDir, lastPullFileName)
data, err := os.ReadFile(path) // #nosec G304 -- path is constrained to the beads directory.
if err != nil {
if os.IsNotExist(err) {
return time.Time{}, nil
}
return time.Time{}, fmt.Errorf("reading last_pull: %w", err)
}
ts := strings.TrimSpace(string(data))
if ts == "" {
return time.Time{}, nil
}
t, err := time.Parse(time.RFC3339, ts)
if err != nil {
return time.Time{}, fmt.Errorf("parsing last_pull timestamp %q: %w", ts, err)
}
return t, nil
}
// IsPullStale returns true if the last pull is older than the given threshold,
// or if no pull has ever been recorded.
func IsPullStale(beadsDir string, threshold time.Duration) bool {
lastPull, err := ReadLastPullTimestamp(beadsDir)
if err != nil || lastPull.IsZero() {
return true
}
return time.Since(lastPull) > threshold
}
// StalenessInfo holds computed staleness details for display purposes.
type StalenessInfo struct {
LastPull time.Time
Age time.DurationView on GitHub (pinned to 71377f2769)
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
Example fix
// before: trust file blindly
t, err := ReadLastPullTimestamp(beadsDir)
// after: self-heal on parse error
t, err := ReadLastPullTimestamp(beadsDir)
if err != nil {
os.Remove(filepath.Join(beadsDir, "last_pull"))
t = time.Time{}
} Defensive patterns
Strategy: fallback
Validate before calling
// validate before relying on the value
if data, err := os.ReadFile(filepath.Join(beadsDir, "last_pull")); err == nil {
if _, perr := time.Parse(time.RFC3339, strings.TrimSpace(string(data))); perr != nil {
os.Remove(filepath.Join(beadsDir, "last_pull"))
}
} Type guard
null
Try / catch
t, err := ReadLastPullTimestamp(beadsDir)
if err != nil {
log.Printf("corrupt last_pull, resetting: %v", err)
os.Remove(filepath.Join(beadsDir, "last_pull"))
t = time.Time{}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- multiple .doltcfg directories detected
- dolt path is not executable
- dolt version output is unparseable
- server: NewDoltServer: failed to determine absolute path of
- server: NewDoltServer: failed to determine absolute path of
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/d8bb0cba7728b8f6.
Report an issue: GitHub.