gastownhall/beads · error
scan dependent: %w
Error message
scan dependent: %w
What it means
While iterating rows returned by the external-dependents query, rows.Scan failed to decode the two expected string columns (depends_on_id, issue_id). This almost always means the row shape returned by the backend does not match the query — e.g. a NULL in a column scanned into a string, or a modified DepTargetExpr changing column type/arity. Rows are closed before returning.
Source
Thrown at internal/storage/issueops/delete_role.go:213
}
inClause, args := buildSQLInClause(ids[i:end])
for _, depTable := range []string{"dependencies", "wisp_dependencies"} {
rows, err := tx.QueryContext(ctx,
fmt.Sprintf(`SELECT %s AS depends_on_id, issue_id FROM %s WHERE %s`,
DepTargetExpr, depTable, depTargetIn("", inClause)),
args...)
if err != nil {
if optionalBlockedTable(depTable) && isTableNotExistError(err) {
continue
}
return nil, fmt.Errorf("query dependents from %s: %w", depTable, err)
}
for rows.Next() {
var target, dependent string
if err := rows.Scan(&target, &dependent); err != nil {
_ = rows.Close()
return nil, fmt.Errorf("scan dependent: %w", err)
}
if idSet[dependent] {
continue
}
if bySource[target] == nil {
bySource[target] = make(map[string]bool)
}
bySource[target][dependent] = true
}
_ = rows.Close()
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate dependents from %s: %w", depTable, err)
}
}
}
out := make(map[string][]string, len(bySource))
for target, dependents := range bySource {View on GitHub (pinned to 71377f2769)
Solutions
- Find the offending row: SELECT ... WHERE issue_id IS NULL OR target IS NULL on the named dependency table, and repair it
- Check whether DepTargetExpr was customized and still yields a plain string column
- Verify dependency rows were not hand-edited or written by an incompatible schema version
- Re-run the delete after repairing the row; the scan fails deterministically on the same row until fixed
Example fix
// before: NULL target breaks the string scan rows, _ := tx.QueryContext(ctx, "SELECT dep_target, issue_id FROM dependencies WHERE ...") // after (repair the data first): UPDATE dependencies SET target_id = '<valid-id>' WHERE target_id IS NULL;
Defensive patterns
Strategy: validation
Validate before calling
// audit dependency rows for NULLs before deleting
rows, err := db.Query("SELECT issue_id, target_id FROM dependencies WHERE issue_id IS NULL OR target_id IS NULL")
// any returned row will crash the scan later; repair it first Try / catch
if err != nil && strings.Contains(err.Error(), "scan dependent") {
// find and fix the malformed dependency row, then re-run
} Prevention
- Never hand-edit dependency tables; use the library's dep-add/remove APIs
- Add a periodic integrity check for NULL dependency columns
- Keep schema versions aligned between writer tools
- After any external import, validate dependency rows before deletes
When it happens
Trigger: ExternalDependentsBySourceInTx loops rows.Next() on the dependencies/wisp_dependencies query and Scan(&target, &dependent) errors — NULL values in scanned columns, driver type mismatch, or altered SELECT shape.
Common situations: Dependency rows with NULL issue_id or NULL target expression written by an older version or external tool; a customized DepTargetExpr returning a non-string type; backend driver returning column types the scan cannot coerce.
Related errors
- scan neighbor: %w
- failed to scan peer for migration: %w
- scan inbound dependency: %w
- get dependents: scan: %w
- get dependency counts: scan blocker: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a47d92618904651b.
Report an issue: GitHub.