benbjohnson/litestream · error
could not parse time: %s
Error message
could not parse time: %s
What it means
parseTimeValue() in vfs.go failed to produce a usable time for a PRAGMA litestream_time value. The value passed the RFC3339 attempts and dateparser.Parse() returned no error, but the resulting time.Time was the zero value, meaning dateparser recognized the string grammatically (or silently failed to match) but could not resolve it to an actual date. The library throws this because a zero time is not a valid time-travel target, so it is rejected with the raw input echoed back.
Source
Thrown at vfs.go:2419
if t, err := time.Parse(time.RFC3339Nano, value); err == nil {
return t, nil
}
// Try RFC3339 (without nanoseconds)
if t, err := time.Parse(time.RFC3339, value); err == nil {
return t, nil
}
// Fall back to dateparser for relative expressions
cfg := &dateparser.Configuration{
CurrentTime: time.Now().UTC(),
}
result, err := dateparser.Parse(cfg, value)
if err != nil {
return time.Time{}, fmt.Errorf("invalid timestamp (expected RFC3339 or relative time like '5 minutes ago'): %s", value)
}
if result.Time.IsZero() {
return time.Time{}, fmt.Errorf("could not parse time: %s", value)
}
return result.Time.UTC(), nil
}
// FileControl handles file control operations, specifically PRAGMA commands for time travel.
func (f *VFSFile) FileControl(op int, pragmaName string, pragmaValue *string) (*string, error) {
const SQLITE_FCNTL_PRAGMA = 14
if op != SQLITE_FCNTL_PRAGMA {
return nil, fmt.Errorf("unsupported file control op: %d", op)
}
name := strings.ToLower(pragmaName)
f.logger.Debug("file control", "pragma", name, "value", pragmaValue)
switch name {
case "litestream_txid":View on GitHub (pinned to 4ed7a308f6)
Solutions
- Wrap the input in try/catch or error-check and print the exact value that failed
- Rewrite the value as RFC3339, e.g. '2024-01-01T10:00:00Z' with a 'T' separator and timezone
- Use a supported relative expression like '5 minutes ago' or '2 hours ago'
- Use the keyword 'latest' to reset time travel to the newest state
- Validate the timestamp string in application code before issuing the PRAGMA
Example fix
// before PRAGMA litestream_time = '2024-01-01 10:00:00'; // after PRAGMA litestream_time = '2024-01-01T10:00:00Z'; // or relative: PRAGMA litestream_time = '5 minutes ago';
Defensive patterns
Strategy: validation
Validate before calling
// Go: validate before issuing PRAGMA litestream_time
func validTimeTravelValue(v string) bool {
if strings.EqualFold(v, "latest") { return true }
if _, err := time.Parse(time.RFC3339Nano, v); err == nil { return true }
if _, err := time.Parse(time.RFC3339, v); err == nil { return true }
// relative forms: e.g. '5 minutes ago', '2 hours ago'
rel := regexp.MustCompile(`(?i)^\d+\s+(second|minute|hour|day|week|month|year)s?\s+ago$`)
return rel.MatchString(v)
} Type guard
func isZeroTime(t time.Time) bool { return t.IsZero() } Try / catch
// driver-level
_, err := db.Exec("PRAGMA litestream_time = ?", value)
if err != nil && strings.Contains(err.Error(), "could not parse time") {
// fall back to a safe default
_, err = db.Exec("PRAGMA litestream_time = 'latest'")
} Prevention
- Always emit RFC3339 with 'T' separator and timezone (e.g. time.Now().UTC().Format(time.RFC3339))
- Prefer 'latest' or simple '<N> <unit>s ago' relative expressions over free-form dates
- Validate timestamps with time.Parse(RFC3339) in the app before issuing the PRAGMA
- Avoid locale-dependent formats like '01/02/2024' or 'yesterday'
When it happens
Trigger: A SQLite connection executes `PRAGMA litestream_time='<value>'` where <value> is not an RFC3339 timestamp and is not a relative expression dateparser can resolve, yet dateparser returns err==nil with a zero Time (e.g. empty-adjacent strings, strings with only a timezone or day name it half-recognizes, or locale-dependent formats it parses to nothing).
Common situations: Developers pass human-friendly but ambiguous values like 'yesterday', 'last week', bare dates ('2024-01-01'), or misspell RFC3339 timestamps ('2024-01-01 10:00:00' with a space instead of T); dateparser partially matches them and yields a zero time instead of an error, surfacing this message rather than the clearer 'invalid timestamp' error.
Related errors
- cannot delete vfs file
- unsupported file control op: %d
- litestream_txid is read-only
- litestream_lag is read-only
- litestream_hydration_progress is read-only
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/e912547c2d1208f1.
Report an issue: GitHub.