gastownhall/beads · error
record dependency_removed event: %w
Error message
record dependency_removed event: %w
What it means
This error wraps a failure from RecordEventInTable while writing the dependency_removed event row after a dependency edge was successfully deleted, when emitEvent was set (explicit 'bd dep remove'). The edge removal itself succeeded but the history event could not be recorded, so the transaction must roll back to keep the dependency table and event history consistent.
Source
Thrown at internal/storage/issueops/dependencies.go:956
return false, fmt.Errorf("lookup dependency type for %s -> %s: %w", issueID, dependsOnID, err)
}
if _, err := tx.ExecContext(ctx, fmt.Sprintf(
`DELETE FROM %s WHERE issue_id = ? AND %s = ?`, depTable, DepTargetExpr),
issueID, dependsOnID); err != nil {
return false, fmt.Errorf("remove dependency: %w", err)
}
// The lookup above returned early when no row matched, so reaching here means
// an edge was actually deleted. Record the dependency_removed event on the
// source issue's event table for bd CLI / library history observers — but only
// when emitEvent is set, so structural removes stay silent (parity with the
// proxied repo and with the symmetric AddDependencyInTx EmitEvent gate).
eventWritten := false
if emitEvent {
if err := RecordEventInTable(ctx, tx, eventTable, issueID, types.EventDependencyRemoved, actor,
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)
}View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped driver error from RecordEventInTable; retry the transaction if transient.
- Run schema migrations to (re)create the events tables with expected columns.
- Free disk space / resolve DB lock contention, then rerun 'bd dep remove'.
- If you only need the structural removal without history, use the structural path (emitEvent=false), which skips event writing entirely.
Example fix
// before: event insert fails on missing events table
ok, err := store.RemoveDependencyInTx(ctx, tx, "bd-1", "bd-2", actor, true)
// after: migrate first, then remove
if err := store.Migrate(ctx); err != nil { return err }
ok, err = store.RemoveDependencyInTx(ctx, tx, "bd-1", "bd-2", actor, true) Defensive patterns
Strategy: try-catch
Validate before calling
// ensure event tables exist and are writable before emitEvent removals
for _, t := range []string{"issues_events", "wisps_events"} {
if err := db.QueryRow("SELECT id FROM " + t + " LIMIT 1").Err(); err != nil {
return fmt.Errorf("event table %s unavailable; migrate first: %w", t, err)
}
} Type guard
func isEventRecordFailure(err error) bool {
return err != nil && strings.Contains(err.Error(), "record dependency_removed event")
} Try / catch
written, err := store.RemoveDependencyInTx(ctx, tx, issueID, dependsOnID, actor, true)
if isEventRecordFailure(err) {
if isTransient(errors.Unwrap(err)) { return retryTx(removalWithEvent) }
// else fall back to structural removal without history
written, err = store.RemoveDependencyInTx(ctx, tx, issueID, dependsOnID, actor, false)
} Prevention
- Keep events tables in the migration set; never prune them manually.
- If only the structural removal matters, use emitEvent=false to skip event writes.
- Monitor disk space — event INSERTs fail after the edge DELETE on a full disk (transaction rolls back).
- Retry the whole transaction on transient event-write failures; state stays consistent.
When it happens
Trigger: RemoveDependencyInTx called with emitEvent=true and the INSERT into the source issue's event table (issues_events or wisps_events per routing) fails — event table missing, event table locked, connection loss, or schema drift on the events table columns.
Common situations: Old database missing the events table or newer event columns; event-table write contention from a concurrent bd process; disk full so the event INSERT fails after the DELETE; manual pruning that dropped event tables.
Related errors
- retarget inbound dependencies to wisp in %s for %s: %w
- affected by status change for %s: %w
- recompute is_blocked after status change for %s: %w
- no store is open for this workspace
- failed to create issue: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/dabd0bcf319357e3.
Report an issue: GitHub.