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, true

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped error for the driver-level cause
  2. Retry the rename — the transaction aborts atomically so no partial rekey persists
  3. Increase query/connection timeouts if the edge count is large
  4. 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

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


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