gastownhall/beads · error

recompute is_blocked after remove dependency %s -> %s: %w

Error message

recompute is_blocked after remove dependency %s -> %s: %w

What it means

Wraps a failure from RecomputeIsBlockedInTxWithResult that occurs after a dependency removal inside a transaction. The library throws it so the caller knows the structural remove succeeded but the derived is_blocked maintenance pass failed; the whole transaction is rolled back with the dependency chain (issueID -> dependsOnID) in the message for diagnosis.

Source

Thrown at internal/storage/issueops/dependencies.go:973

			fmt.Sprintf("Removed dependency on %s", dependsOnID)); err != nil {
			return false, fmt.Errorf("record dependency_removed event: %w", err)
		}
		eventWritten = true
	}

	var affectedIssues, affectedWisps []string
	var aerr error
	if isWisp {
		affectedIssues, affectedWisps, aerr = AffectedByDepChangeForWispInTx(ctx, tx, issueID, dependsOnID, types.DependencyType(depType))
	} else {
		affectedIssues, affectedWisps, aerr = AffectedByDepChangeInTx(ctx, tx, issueID, dependsOnID, types.DependencyType(depType))
	}
	if aerr != nil {
		return false, fmt.Errorf("affected by remove dependency %s -> %s: %w", issueID, dependsOnID, aerr)
	}
	recomputed, err := RecomputeIsBlockedInTxWithResult(ctx, tx, affectedIssues, affectedWisps)
	if err != nil {
		return false, fmt.Errorf("recompute is_blocked after remove dependency %s -> %s: %w", issueID, dependsOnID, err)
	}
	mergeRecomputeIsBlockedResult(recomputeResult, recomputed)
	// Snapshot only after all derived blocked-state maintenance has completed.
	// Never gated on emitEvent — a structural removal is as real to a replaying
	// consumer as one from an explicit dep verb.
	return eventWritten, RecordDepEventInTx(ctx, tx, EventDepRemove, issueID, depType, dependsOnID, depMetadata, actor)
}

func mergeRecomputeIsBlockedResult(target *RecomputeIsBlockedResult, source RecomputeIsBlockedResult) {
	if target == nil {
		return
	}
	target.IssueRowsChanged = target.IssueRowsChanged || source.IssueRowsChanged
	target.WispRowsChanged = target.WispRowsChanged || source.WispRowsChanged
}

// GetIssuesByIDsInTx retrieves multiple issues by ID within an existing
// transaction, including labels. Automatically routes each ID to the correct

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause (%w) — it is almost always a lower-level DB error from the recompute query; fix that root cause first
  2. Retry the operation; deadlocks and lock timeouts during recompute are often transient
  3. Verify dependency table integrity (orphaned dependencies/wisp rows) if the error persists
  4. Check DB connectivity and transaction timeout settings if errors cluster under load
Defensive patterns

Strategy: retry

Validate before calling

// Before removing, confirm the dependency exists and the DB is reachable:
if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("db unreachable, skip dep removal: %w", err)
}
// Optionally pre-check the dep row:
var n int
_ = db.QueryRowContext(ctx, `SELECT COUNT(*) FROM dependencies WHERE issue_id = ? AND depends_on_id = ?`, issueID, dependsOnID).Scan(&n)
if n == 0 { return nil } // nothing to remove, no recompute risk

Try / catch

for attempt := 0; attempt < 3; attempt++ {
    _, err := ops.RemoveDependencyInTx(ctx, tx, issueID, dependsOnID, depType)
    if err == nil { break }
    if !isTransientDBError(err) { return err } // errors contain "recompute is_blocked after remove dependency"
    time.Sleep(backoff(attempt))
}

Prevention

When it happens

Trigger: removeDependencyInTx (via ApplyParentPatch or RemoveDependencyInTx) successfully deletes the dependency row and identifies affected issues/wisps, but RecomputeIsBlockedInTxWithResult returns an error while recomputing is_blocked for those affected issues.

Common situations: Database-level failures during the recompute (deadlock, lock wait timeout, connection drop mid-transaction), a corrupted dependency graph (missing rows the recompute expects), or driver errors scanning dependency rows for the affected set.

Related errors


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