gastownhall/beads · error
iterate dependency sources: %w
Error message
iterate dependency sources: %w
What it means
This error wraps rows.Err() after iterating the dependency-source result set in rekeyDependencySourceInTx. It surfaces driver/stream errors that occur mid-iteration (connection drop, query killed, replication error) rather than at query time or during a single Scan.
Source
Thrown at internal/storage/issueops/dependencies.go:664
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)
}
}
return nil
}
// resolveDependencyTarget returns the single non-null dependency target — the value
// depid.New and the uk_dep_* unique keys treat as the edge's target — following the
// same precedence as DepTargetExpr (issue, then wisp, then external).
func resolveDependencyTarget(issueTarget, wispTarget, external sql.NullString) (string, bool) {
switch {
case issueTarget.Valid:
return issueTarget.String, trueView on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped error for the driver-level cause
- Retry the rename — the transaction aborts atomically so no partial rekey persists
- Increase query/connection timeouts if the edge count is large
- Check DB server logs for killed queries or connection resets at the matching timestamp
Defensive patterns
Strategy: retry
Validate before calling
// Sanity check connectivity and expected edge volume before starting
if err := db.PingContext(ctx); err != nil {
return fmt.Errorf("database unreachable, postpone rename: %w", err)
}
var n int
db.Get(&n, `SELECT COUNT(*) FROM dependencies WHERE issue_id IN (?, ?) OR depends_on_issue_id = ?`, oldID, newID, oldID)
log.Printf("rename will touch %d dependency rows", n) Try / catch
for attempt := 0; attempt < 3; attempt++ {
err := updateIssueID(tx, oldID, newID)
if err == nil { break }
if !isTransientDriverError(err) { return err }
time.Sleep(backoff(attempt)) // transaction rolled back; safe to retry
} Prevention
- Set generous connect/read timeouts for Dolt/MySQL before bulk renames
- Retry whole renames, never partial steps — the transaction makes retries safe
- Avoid renaming issues with very large fan-in/out over unstable networks
- Watch server logs for killed queries and raise max_execution_time if needed
When it happens
Trigger: UpdateIssueIDInTx rename, while streaming rows from `dependencies WHERE issue_id = ? OR issue_id = ?`: the underlying connection or result stream fails after some rows were already read.
Common situations: Long-running rename over many dependency edges with an unstable DB connection; server-side query timeout (max_execution_time); Dolt replica failover mid-scan.
Related errors
- iterate dependency targets: %w
- count edges in %s: rows: %w
- search %s (hydrate): rows: %w
- db: ChildCounterSQLRepository.NextChildID: rows: %w
- db: CommentSQLRepository.CountsByIssueIDs: rows: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/cc123ce0f5b2797d.
Report an issue: GitHub.