gastownhall/beads · error
legacy SQLite issue %s updated_at: %w
Error message
legacy SQLite issue %s updated_at: %w
What it means
Companion to the created_at normalization error: applyCanonicalTimestamps failed to normalize the issue's updated_at value to the canonical current-schema datetime. Migration will not import an issue whose last-updated timestamp cannot be parsed, since updated_at is required by the current schema.
Source
Thrown at internal/migration/legacysqlite/reader.go:544
}
waiters, err := decodeWaiters(x.waiters.String)
if err != nil {
return fmt.Errorf("issue %s waiters: %w", issue.ID, err)
}
issue.Waiters = waiters
}
return nil
}
// applyCanonicalTimestamps normalizes the required created_at/updated_at values
// to the canonical current-schema representation.
func (x legacyExtras) applyCanonicalTimestamps(issue *types.Issue) error {
var err error
if issue.CreatedAt, err = canonicalCurrentDatetime(issue.CreatedAt); err != nil {
return fmt.Errorf("legacy SQLite issue %s created_at: %w", issue.ID, err)
}
if issue.UpdatedAt, err = canonicalCurrentDatetime(issue.UpdatedAt); err != nil {
return fmt.Errorf("legacy SQLite issue %s updated_at: %w", issue.ID, err)
}
return nil
}
// checkRequiredScalars enforces the non-empty ID, non-tombstone status, and
// present created_at/updated_at invariants every legacy issue must satisfy.
func checkRequiredScalars(issue *types.Issue) error {
if issue.ID == "" {
return fmt.Errorf("legacy SQLite issue has empty ID")
}
if issue.Status == "tombstone" {
return fmt.Errorf("legacy SQLite issue %s is a tombstone", issue.ID)
}
if issue.CreatedAt.IsZero() || issue.UpdatedAt.IsZero() {
return fmt.Errorf("legacy SQLite issue %s has invalid created_at or updated_at", issue.ID)
}
return nil
}View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the raw value: sqlite3 legacy.db "SELECT id, updated_at FROM issues WHERE id='<id>'"
- Rewrite updated_at as a valid ISO-8601 UTC timestamp, e.g. '2024-06-01T12:00:00Z'
- Use created_at as a fallback value if the true update time is unrecoverable
- Re-run the migration
Example fix
// before updated_at = '1717243200000' // after updated_at = '2024-06-01T12:00:00Z'
Defensive patterns
Strategy: validation
Validate before calling
from datetime import datetime
def ts_ok(v):
if not v:
return False
try:
datetime.fromisoformat(v.replace('Z', '+00:00'))
return True
except ValueError:
return False
# bad = [r['id'] for r in rows if not ts_ok(r['updated_at'])] Type guard
def is_canonical_datetime(v) -> bool:
if not isinstance(v, str) or not v:
return False
try:
datetime.fromisoformat(v.replace('Z', '+00:00'))
return True
except ValueError:
return False Prevention
- Audit both created_at and updated_at columns in one pass
- Backfill missing/odd timestamps before migration
- Keep timestamps in ISO-8601 UTC everywhere
When it happens
Trigger: Legacy row's updated_at is empty, NULL-derived, or in a format canonicalCurrentDatetime cannot parse (non-ISO string, integer epoch in unexpected units, etc.).
Common situations: Third-party edits to the legacy DB, very old schema versions with different timestamp encoding, or partial writes from a crashed process.
Related errors
- legacy SQLite issue %s created_at: %w
- legacy SQLite issue %s has invalid created_at or updated_at
- sealed legacy SQLite database does not match source fingerpr
- sealed legacy SQLite WAL does not match source fingerprint
- legacy SQLite source changed while sealing
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/786526d1193b05a3.
Report an issue: GitHub.