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

  1. 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 = '')
  2. Re-run migration after repair; if parseTime still fails on format, normalize the column format to the expected RFC3339-style layout
  3. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/a97b93fbbbb5280d. Report an issue: GitHub.