gastownhall/beads · error
dependency created_at is zero for %s -> %s
Error message
dependency created_at is zero for %s -> %s
What it means
The migration reader parses each dependency's created_at timestamp and rejects the import when the parsed time is the zero time.Time. A zero timestamp means the legacy row has no usable creation date, and bd requires every dependency to carry a real CreatedAt for ordering and display.
Source
Thrown at internal/migration/legacysqlite/reader.go:920
}
issue := byID[id]
if issue == nil || byID[to] == nil {
return fmt.Errorf("orphan dependency %s -> %s", id, to)
}
if issue.Ephemeral != byID[to].Ephemeral {
return fmt.Errorf("dependency %s -> %s crosses ephemeral storage", id, to)
}
key := id + "\x00" + to
if seenDeps[key] {
return fmt.Errorf("multiple legacy dependencies for %s -> %s", id, to)
}
seenDeps[key] = true
created, e := parseTime(at)
if e != nil {
return e
}
if created.IsZero() {
return fmt.Errorf("dependency created_at is zero for %s -> %s", id, to)
}
if (metadata.Valid && metadata.String != "") || (thread.Valid && thread.String != "") {
return fmt.Errorf("dependency %s -> %s uses unsupported metadata or thread ID", id, to)
}
if !types.DependencyType(typ).IsValid() {
return fmt.Errorf("dependency %s -> %s has invalid type", id, to)
}
d := &types.Dependency{IssueID: id, DependsOnID: to, Type: types.DependencyType(typ), CreatedAt: created, CreatedBy: by, Metadata: nullString(metadata), ThreadID: nullString(thread)}
issue.Dependencies = append(issue.Dependencies, d)
return nil
}
func loadComments(ctx context.Context, db *sql.Tx, byID map[string]*types.Issue) error {
comments, err := db.QueryContext(ctx, "SELECT id,issue_id,author,text,CAST(created_at AS TEXT) FROM comments ORDER BY issue_id,created_at,id")
if err != nil {
return err
}
defer comments.Close()View on GitHub (pinned to 71377f2769)
Solutions
- Backfill created_at for the offending dependency rows in the legacy DB (e.g. UPDATE dependencies SET created_at = <iso timestamp> WHERE created_at IS NULL OR created_at = '')
- Re-run migration after repair; if parseTime still fails on format, normalize the column format to the expected RFC3339-style layout
- Exclude/recreate the dependency manually in bd after migration if the timestamp cannot be recovered
Example fix
-- before
-- dependencies row: ('bd-1','bd-2',NULL)
UPDATE dependencies SET created_at = '2024-01-15T10:00:00Z'
WHERE created_at IS NULL OR created_at = '';
-- after: row has a real timestamp Defensive patterns
Strategy: validation
Validate before calling
for (const d of legacyDeps) {
if (!d.created_at || new Date(d.created_at).getTime() === 0 || isNaN(Date.parse(d.created_at)))
throw new Error(`zero/invalid created_at on ${d.issue_id} -> ${d.depends_on_id}`);
} Type guard
function hasCreatedAt(d) {
return typeof d.created_at === 'string' && d.created_at !== '' && !isNaN(Date.parse(d.created_at));
} Try / catch
try { migrateLegacySQLite(dbPath) } catch (e) {
if (e.message.includes('created_at is zero')) {
const m = e.message.match(/for (\S+) -> (\S+)/);
backfillDepCreatedAt(dbPath, m[1], m[2]);
retry();
} else throw e;
} Prevention
- Backfill NULL/empty created_at columns before migration
- Enforce NOT NULL on created_at in the legacy schema
- Normalize all timestamps to RFC3339 before importing
When it happens
Trigger: Legacy SQLite dependency rows whose created_at column is NULL, empty, or stored in a format `parseTime` resolves to the zero value; the pair id -> to is reported.
Common situations: Very old legacy databases predating the created_at column on dependencies; rows hand-inserted via SQL without a timestamp; corrupted or out-of-format date strings.
Related errors
- timestamp %q: %w
- invalid timestamp %q
- timestamp rounds outside current DATETIME range
- %s: %w
- comment created_at is zero for issue %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a97b93fbbbb5280d.
Report an issue: GitHub.