gastownhall/beads · error
invalid timestamp %q
Error message
invalid timestamp %q
What it means
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.
Source
Thrown at internal/migration/legacysqlite/reader.go:788
return false
}
func nullString(v sql.NullString) string {
if v.Valid {
return v.String
}
return ""
}
func parseTime(s string) (time.Time, error) {
for _, layout := range []string{time.RFC3339Nano, "2006-01-02 15:04:05.999999999-07:00", "2006-01-02 15:04:05.999999999"} {
if t, e := time.Parse(layout, s); e == nil {
canonical, err := canonicalCurrentDatetime(t)
if err != nil {
return time.Time{}, fmt.Errorf("timestamp %q: %w", s, err)
}
return canonical, nil
}
}
return time.Time{}, fmt.Errorf("invalid timestamp %q", s)
}
func canonicalCurrentDatetime(t time.Time) (time.Time, error) {
canonical := t.UTC().Round(time.Second)
if year := canonical.Year(); year < 0 || year > 9999 {
return time.Time{}, fmt.Errorf("timestamp rounds outside current DATETIME range")
}
return canonical, nil
}
func parseOptionalTime(name string, raw sql.NullString) (*time.Time, error) {
if !raw.Valid {
return nil, nil
}
parsed, err := parseTime(raw.String)
if err != nil {
return nil, fmt.Errorf("%s: %w", name, err)
}View on GitHub (pinned to 71377f2769)
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).
Example fix
// before: created_at = '1714560000' (unix epoch)
UPDATE 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 '*-*';
// after: parseable datetime text Defensive patterns
Strategy: validation
Validate before calling
func parseableTimestamp(s string) bool {
for _, layout := range []string{time.RFC3339Nano, "2006-01-02 15:04:05.999999999-07:00", "2006-01-02 15:04:05.999999999"} {
if _, err := time.Parse(layout, s); err == nil {
return true
}
}
return false
}
// scan created_at/updated_at/closed_at columns with this before migrating Try / catch
if err := migrateLegacy(db); err != nil {
if strings.Contains(err.Error(), "invalid timestamp") {
// extract %q value from message, fix that row, retry
return fmt.Errorf("normalize timestamps in legacy DB and retry: %w", err)
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- timestamp %q: %w
- timestamp rounds outside current DATETIME range
- %s: %w
- dependency created_at is zero for %s -> %s
- comment created_at is zero for issue %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/48b158bdc2078cc5.
Report an issue: GitHub.