gastownhall/beads · error
db: Update %s: affected by status change: %w
Error message
db: Update %s: affected by status change: %w
What it means
This error wraps a failure while computing which issues are affected by a status change during Update. A status change can alter blocking relationships, so the storage layer first queries the affected set (via issueops.AffectedByStatusChangeForWispInTx or AffectedByStatusChangeInTx) inside the same transaction before recomputing is_blocked. If that lookup fails, the entire Update transaction is aborted with this wrapped error.
Source
Thrown at internal/storage/domain/db/issue.go:321
return err
}
if statusChanging {
newStatus := coerceStatus(updates["status"])
oldActive := oldIssue.Status != types.StatusClosed && oldIssue.Status != types.StatusPinned
newActive := newStatus != types.StatusClosed && newStatus != types.StatusPinned
if oldActive != newActive {
var (
affectedIssues, affectedWisps []string
aerr error
)
if opts.UseWispsTable {
affectedIssues, affectedWisps, aerr = issueops.AffectedByStatusChangeForWispInTx(ctx, r.runner, id)
} else {
affectedIssues, affectedWisps, aerr = issueops.AffectedByStatusChangeInTx(ctx, r.runner, id)
}
if aerr != nil {
return fmt.Errorf("db: Update %s: affected by status change: %w", id, aerr)
}
if err := issueops.RecomputeIsBlockedInTx(ctx, r.runner, affectedIssues, affectedWisps); err != nil {
return fmt.Errorf("db: Update %s: recompute is_blocked: %w", id, err)
}
}
}
// Snapshot only after all derived blocked-state maintenance has completed.
// The no-op early returns above wrote nothing and journal nothing.
return issueops.RecordEventInTx(ctx, r.runner, issueops.EventUpdate, id, actor)
}
// CompareAndSetMetadataKey runs the SHARED compare-and-set body, unwrapped.
//
// It is the whole of this leg's implementation, and that is the point: the two
// store backends wrap the same function in their own transaction, so the third
// leg is a wrapper check rather than an independent vote — which is what the
// conformance contract's header says.
//View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped cause (%w) for the underlying SQL/driver error and fix that first.
- Retry the update once the database is reachable; the transaction rolled back so state is consistent.
- Verify the issues/wisps and dependency tables match expected migrations.
- Check UseWispsTable routing — ensure the correct table variant exists for the store.
Example fix
// before
err := store.Update(ctx, id, opts) // opaque failure mid-status-change
// after
if err := store.Update(ctx, id, opts); err != nil {
if errors.Is(err, context.DeadlineExceeded) {
err = store.Update(ctx, id, opts) // safe: transactional, rolled back
}
} Defensive patterns
Strategy: retry
Validate before calling
if _, err := store.Get(ctx, id, nil); err != nil { return err }
if err := ctx.Err(); err != nil { return err } Try / catch
if err := store.Update(ctx, id, opts); err != nil {
var transient = errors.Is(err, context.DeadlineExceeded) || errors.Is(err, io.ErrUnexpectedEOF)
if transient { err = store.Update(ctx, id, opts) }
} Prevention
- Keep update contexts generously timed for status changes with many dependents.
- Keep schema migrations current before running updates.
- Avoid manual edits to dependency tables.
- Use one canonical code path for status updates so wisp routing stays correct.
When it happens
Trigger: Calling Update on an issue with a status-changing opts while AffectedByStatusChange(InTx/ForWispInTx) errors: transaction aborted, connection dropped, lock-wait timeout, or SQL failure in the dependent lookup.
Common situations: Connection timeouts or server restarts mid-transaction during 'bd update' of a status field; schema drift in dependency tables; concurrent writers causing lock contention; wisp-table routing errors when UseWispsTable is true.
Related errors
- ErrTransaction
- open unit of work: %w
- failed to begin transaction: %w
- failed to commit is_blocked repairs: %w
- failed to begin transaction: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/516fd91da3f51160.
Report an issue: GitHub.