gastownhall/beads · error

failed to get parent-child deps from

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.

Solutions

  1. Note the table name in the message and inspect its schema against bd's expected columns.
  2. Run pending migrations so dependencies/wisp_dependencies match the current schema.
  3. Retry if the wrapped cause is a transient lock or connection error.
  4. 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

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


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

Appendix: 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)