gastownhall/beads · error
failed to get parent-child deps from %s: %w
Error message
failed to get parent-child deps from %s: %w
What it means
The function then loads parent-child dependencies from each dependencies table (issues and, optionally, wisps). If querying a table fails — and it is not the tolerable 'wisp_dependencies missing on pre-migration DB' case — the error is wrapped with the offending table name. It means the parent-child relationship data could not be read, so closure eligibility cannot be computed.
Source
Thrown at internal/storage/issueops/epic_closure.go:54
// Step 2: Get parent-child dependencies from both tables (bd-w2w)
// Wisp children store their parent-child deps in wisp_dependencies,
// so we must check both tables to find all children of an epic.
epicChildMap := make(map[string][]string)
epicSet := make(map[string]bool, len(epicIDs))
for _, id := range epicIDs {
epicSet[id] = true
}
for _, depTable := range []string{"dependencies", "wisp_dependencies"} {
depRows, err := tx.QueryContext(ctx, fmt.Sprintf(`
SELECT %s AS parent_id, issue_id FROM %s
WHERE type = 'parent-child' AND %s IS NOT NULL
`, DepTargetExpr, depTable, DepTargetExpr))
if err != nil {
if optionalBlockedTable(depTable) && isTableNotExistError(err) {
continue // wisp_dependencies may not exist on pre-migration databases
}
return nil, fmt.Errorf("failed to get parent-child deps from %s: %w", depTable, err)
}
for depRows.Next() {
var parentID, childID string
if err := depRows.Scan(&parentID, &childID); err != nil {
depRows.Close()
return nil, fmt.Errorf("scan parent-child dep from %s: %w", depTable, err)
}
if epicSet[parentID] {
epicChildMap[parentID] = append(epicChildMap[parentID], childID)
}
}
depRows.Close()
}
// Step 3: Batch-fetch statuses for all child issues across all epics
allChildIDs := make([]string, 0)
for _, children := range epicChildMap {
allChildIDs = append(allChildIDs, children...)View on GitHub (pinned to 71377f2769)
Solutions
- Note the table name in the message and inspect its schema against bd's expected columns.
- Run pending migrations so dependencies/wisp_dependencies match the current schema.
- Retry if the wrapped cause is a transient lock or connection error.
- Check the wrapped error for 'unknown column' and repair or recreate the dependencies table.
Defensive patterns
Strategy: validation
Validate before calling
for _, t := range []string{"dependencies", "wisp_dependencies"} {
if !tableExists(db, t) { return fmt.Errorf("%s missing; run migrations", t) }
} Type guard
func isDepQueryErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "failed to get parent-child deps from")
} Try / catch
epics, err := GetEpicsEligibleForClosureInTx(ctx, tx)
if err != nil {
if isDepQueryErr(err) {
if table := extractTable(err); !tableExists(db, table) { return migrateThenRetry(ctx) }
}
return err
} Prevention
- Keep the schema current with bd releases before running closure jobs.
- Avoid concurrent writers during dependency-graph reads.
- Verify dependencies table columns match bd's expected DepTargetExpr columns after upgrades.
- Backup the DB before schema-affecting operations.
When it happens
Trigger: Calling GetEpicsEligibleForClosureInTx when the `SELECT ... FROM <depTable> WHERE type='parent-child'` query fails on a table that does exist — connection error, missing expected columns (DepTargetExpr), or a corrupt dependencies table.
Common situations: Schema drift where `dependencies` lacks columns bd expects (older bd created the DB); database locks from a concurrent writer; wisp_dependencies existing but malformed.
Understand the failure class
Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.
Related errors
- failed to recompute is_blocked: %w
- count wisp dependencies: %w
- count labels: %w
- count wisp labels: %w
- count events: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/5966565157703667.
Report an issue: GitHub.