gastownhall/beads · error
orphan dependency %s -> %s
Error message
orphan dependency %s -> %s
What it means
A dependency row references an issue_id or depends_on_id that does not exist among the loaded issues (byID map). The reader refuses to import dangling dependency edges because they would violate the current database's foreign-key integrity and produce a broken dependency graph.
Source
Thrown at internal/migration/legacysqlite/reader.go:905
currentString{"dependency type", typ},
currentString{"dependency created_by", by},
); err != nil {
return err
}
if err := checkCurrentVarchars(
currentVarchar{"dependency issue_id", id, types.MaxFieldLen},
currentVarchar{"dependency depends_on_id", to, types.MaxFieldLen},
currentVarchar{"dependency type", typ, currentShortVarcharRunes},
currentVarchar{"dependency created_by", by, types.MaxFieldLen},
); err != nil {
return err
}
if by == "" {
return fmt.Errorf("dependency created_by is empty for %s -> %s", id, to)
}
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)View on GitHub (pinned to 71377f2769)
Solutions
- Remove dangling edges before migrating: DELETE FROM dependencies WHERE issue_id NOT IN (SELECT id FROM issues) OR depends_on_id NOT IN (SELECT id FROM issues).
- If the edge is still meaningful, re-point the missing endpoint to the correct existing issue ID.
- Run PRAGMA foreign_key_check on the legacy DB to enumerate all integrity violations in one pass.
- Fix the deletion workflow that produced orphans (enable FK enforcement or always clean child rows) so it does not recur.
Example fix
// before: edges pointing at deleted issues DELETE FROM dependencies WHERE issue_id NOT IN (SELECT id FROM issues) OR depends_on_id NOT IN (SELECT id FROM issues); // after: all endpoints resolve
Defensive patterns
Strategy: validation
Validate before calling
rows, err := legacyDB.Query(`SELECT issue_id, depends_on_id FROM dependencies
WHERE issue_id NOT IN (SELECT id FROM issues)
OR depends_on_id NOT IN (SELECT id FROM issues)`)
// delete or re-point these dangling edges before migrating Try / catch
if err := migrateLegacy(db); err != nil {
if strings.Contains(err.Error(), "orphan dependency") {
return fmt.Errorf("remove dangling dependency edges and retry: %w", err)
}
return err
} Prevention
- Enable PRAGMA foreign_keys = ON on the legacy database to prevent dangling edges at write time.
- Run PRAGMA foreign_key_check as a pre-migration integrity gate.
- Clean up dependencies whenever issues are deleted or IDs change.
When it happens
Trigger: The legacy SQLite dependencies table contains an edge whose either endpoint was deleted (or renamed) without removing the edge, detected in appendLegacyDependencyRow when byID[id] or byID[to] is nil after the created_by check.
Common situations: Legacy versions without foreign-key enforcement deleting issues directly via SQL; bulk cleanup scripts that removed issues but left dependencies; corrupted databases from crashed old versions; test fixtures with hand-written inconsistent edges.
Related errors
- orphan label for %s
- dependency created_by is empty for %s -> %s
- dependency %s -> %s crosses ephemeral storage
- orphan comment for %s
- sealed legacy SQLite database does not match source fingerpr
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c26e3069924caba2.
Report an issue: GitHub.