gastownhall/beads · error
scan dependency source: %w
Error message
scan dependency source: %w
What it means
This error wraps a rows.Scan failure while reading a dependency source row (id plus the three nullable target columns) in rekeyDependencySourceInTx. Scan fails when the stored value's type does not match the scan destination (e.g. non-string id, corrupt NULL in a NOT NULL column) or the column count mismatches the schema.
Source
Thrown at internal/storage/issueops/dependencies.go:652
// in case a caller reaches here before the cascade) and re-asserts issue_id = newID
// so the row converges either way. Only rows whose id is actually stale are touched.
func rekeyDependencySourceInTx(ctx context.Context, tx *sql.Tx, oldID, newID string) error {
queryRows, err := tx.QueryContext(ctx, `
SELECT id, depends_on_issue_id, depends_on_wisp_id, depends_on_external
FROM dependencies
WHERE issue_id = ? OR issue_id = ?
`, newID, oldID)
if err != nil {
return fmt.Errorf("query dependency sources: %w", err)
}
type rekey struct{ oldRowID, newRowID string }
var rekeys []rekey
for queryRows.Next() {
var id string
var issueTarget, wispTarget, external sql.NullString
if err := queryRows.Scan(&id, &issueTarget, &wispTarget, &external); err != nil {
_ = queryRows.Close()
return fmt.Errorf("scan dependency source: %w", err)
}
target, ok := resolveDependencyTarget(issueTarget, wispTarget, external)
if !ok {
continue // ck_dep_one_target guarantees one target; skip defensively
}
if want := depid.New(newID, target); want != id {
rekeys = append(rekeys, rekey{oldRowID: id, newRowID: want})
}
}
_ = queryRows.Close()
if err := queryRows.Err(); err != nil {
return fmt.Errorf("iterate dependency sources: %w", err)
}
for _, rk := range rekeys {
if _, err := tx.ExecContext(ctx,
"UPDATE dependencies SET id = ?, issue_id = ? WHERE id = ?",
rk.newRowID, newID, rk.oldRowID); err != nil {
return fmt.Errorf("rekey dependency source id %s -> %s: %w", rk.oldRowID, rk.newRowID, err)View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped scan error to see which column/type mismatched
- Run bd doctor to detect and repair schema/data inconsistencies
- Dump and inspect the offending dependencies row: SELECT * FROM dependencies WHERE issue_id IN (oldID, newID)
- Restore the row from a backup or delete/recreate the malformed dependency edge
Example fix
// before: corrupt row breaks rename default_params: id column holds an integer where a string is expected -> scan dependency source: sql: Scan error // after: repair the row UPDATE dependencies SET id = CAST(id AS CHAR) WHERE id REGEXP '^[0-9]+$'; -- or restore from backup
Defensive patterns
Strategy: validation
Validate before calling
// Detect rows that cannot be scanned into the expected types
var bad int
err := db.Get(&bad, `SELECT COUNT(*) FROM dependencies
WHERE id IS NULL OR id = ''
OR (depends_on_issue_id IS NULL AND depends_on_wisp_id IS NULL AND depends_on_external IS NULL)`)
if err != nil { return err }
if bad > 0 { return fmt.Errorf("%d malformed dependency rows; run bd doctor before renaming issues", bad) } Prevention
- Never hand-edit the dependencies table with raw SQL
- Run bd doctor after version upgrades or clone/merge operations
- Restore corrupt rows from backup before attempting renames
- Keep all replicas in sync; scan mismatches often indicate replication divergence
When it happens
Trigger: During UpdateIssueIDInTx, a row in dependencies matching issue_id = oldID OR issue_id = newID has a value that cannot be scanned into string or sql.NullString — typically corrupt or schema-divergent data.
Common situations: Database written by an incompatible older/newer beads version; manual SQL edits that changed column types; Dolt replication divergence producing malformed rows.
Related errors
- search %s (id scan): scan: %w
- scan dependency target: %w
- %s: %w
- failed to scan federation peer: %w
- db: ChildCounterSQLRepository.NextChildID: scan: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/87cc53a3a742bfb7.
Report an issue: GitHub.