gastownhall/beads · error

journal: iterate dependency removals from %s: %w

Error message

journal: iterate dependency removals from %s: %w

What it means

This error wraps a rows.Err() failure encountered while iterating the result set of dependency-removal rows read from a journal table inside dependencyEdgesInTableForIssueIDsInTx. The library throws it because a streaming row iteration can fail mid-scan (connection drop, driver decode error), and such failures must abort the rebuild of dependency edges rather than silently return a partial set. The wrapped table name identifies which journal table ('dependencies' or wisp variant) the iteration failed on.

Source

Thrown at internal/storage/issueops/journal_dependencies.go:105

			WHERE issue_id IN (%s) OR %s IN (%s)
		`, DepTargetExpr, table, inClause, DepTargetExpr, inClause), queryArgs...)
		if err != nil {
			if optionalBlockedTable(table) && isTableNotExistError(err) {
				continue
			}
			return nil, fmt.Errorf("journal: query dependency removals from %s: %w", table, err)
		}
		for rows.Next() {
			var edge journalDependencyEdge
			if err := rows.Scan(&edge.source, &edge.target, &edge.kind, &edge.metadata); err != nil {
				_ = rows.Close()
				return nil, fmt.Errorf("journal: scan dependency removal from %s: %w", table, err)
			}
			byKey[dependencyEdgeKey(edge)] = edge
		}
		if err := rows.Err(); err != nil {
			_ = rows.Close()
			return nil, fmt.Errorf("journal: iterate dependency removals from %s: %w", table, err)
		}
		if err := rows.Close(); err != nil {
			return nil, fmt.Errorf("journal: close dependency removals from %s: %w", table, err)
		}
	}
	return sortedDependencyEdges(byKey), nil
}

func dependencyEdgeKey(edge journalDependencyEdge) string {
	return edge.source + "\x00" + edge.target + "\x00" + edge.kind + "\x00" + edge.metadata
}

func sortedDependencyEdges(byKey map[string]journalDependencyEdge) []journalDependencyEdge {
	edges := make([]journalDependencyEdge, 0, len(byKey))
	for _, edge := range byKey {
		edges = append(edges, edge)
	}
	sort.Slice(edges, func(i, j int) bool {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check database connectivity and retry the operation once the connection is healthy
  2. Increase the context timeout for the transaction so iteration is not cut off
  3. Inspect the wrapped error (%w chain) for driver-specific causes (driver.ErrBadConn, context.DeadlineExceeded)
  4. Reduce batch size of issue IDs to shrink result sets on slow remote backends
Defensive patterns

Strategy: retry

Validate before calling

if err := ctx.Err(); err != nil { return fmt.Errorf("context already done before dependency rebuild: %w", err) }

Try / catch

edges, err := dependencyEdgesForIssueIDsInTx(ctx, tx, ids)
if err != nil {
    var netErr net.Error
    if errors.As(err, &netErr) || errors.Is(err, context.DeadlineExceeded) {
        // transient: retry with fresh connection/backoff
    }
    return err
}

Prevention

When it happens

Trigger: Calling RecordDependencyRemovalsForTableInTx or dependencyEdgesForIssueIDsInTx, which invoke dependencyEdgesInTableForIssueIDsInTx, when the underlying rows iterator returns a non-nil error after rows.Next() exhausts — e.g. the Dolt/MySQL connection breaks mid-result or the context deadline expires during iteration.

Common situations: Remote Dolt backends dropping connections on large result sets; context cancellation/timeouts during long transactions (GH#3414-style round-trip latency); server restarts mid-query.

Related errors


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