gastownhall/beads · error
failed to update issue: %w
Error message
failed to update issue: %w
What it means
The UPDATE statement executed inside updateIssueInTx failed at the database driver level. The driver error is wrapped as "failed to update issue: %w", and the transaction will typically be rolled back by the caller.
Source
Thrown at internal/storage/issueops/update.go:490
// Auto-manage leases when direct updates change status or assignee.
// Clears stale leases only; arming is reserved for claim/heartbeat.
clearLease := ManageLeaseOnUpdate(oldIssue, updates)
// Rewrite row_lock on every update so a concurrent status/ownership
// mutation (reclaim/close) collides on this shared cell and is forced to
// conflict-and-retry rather than silently cell-merging two writes to
// different columns of the same row (see lease.go). This is the "every
// mutating path writes row_lock" invariant the lease scheme depends on.
setClauses = append(setClauses, "row_lock = ?")
args = append(args, freshRowLock())
args = append(args, id)
//nolint:gosec // G201: issueTable comes from WispTableRouting (hardcoded constants)
query := fmt.Sprintf("UPDATE %s SET %s WHERE id = ?", issueTable, strings.Join(setClauses, ", "))
if _, err := tx.ExecContext(ctx, query, args...); err != nil {
return nil, fmt.Errorf("failed to update issue: %w", err)
}
if clearLease {
if err := DeleteLeaseInTx(ctx, tx, id); err != nil {
return nil, err
}
}
if recordEvent {
oldData, _ := json.Marshal(oldIssue)
newData, _ := json.Marshal(updates)
eventType := DetermineEventType(oldIssue, updates)
if err := RecordFullEventInTable(ctx, tx, eventTable, id, eventType, actor, string(oldData), string(newData)); err != nil {
return nil, fmt.Errorf("failed to record event: %w", err)
}
}
View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped driver error for the SQL state/constraint message.
- Verify all update values match column types and constraints (run the equivalent UPDATE manually).
- Check database connectivity and retry the transaction if the error is transient (connection refused/timeout).
- Confirm the schema matches the version of the code (run migrations/bd doctor).
Example fix
// before
if _, err := tx.ExecContext(ctx, query, args...); err != nil {
return nil, fmt.Errorf("failed to update issue: %w", err) // opaque
}
// after
if _, err := tx.ExecContext(ctx, query, args...); err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return nil, err // retryable: caller re-runs tx
}
return nil, fmt.Errorf("failed to update issue: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
// pre-check values against schema constraints before the call
if s, ok := updates["status"]; ok {
if !types.IsValidStatus(fmt.Sprintf("%v", s)) {
return fmt.Errorf("invalid status %v", s)
}
} Try / catch
if _, err := storage.UpdateIssue(ctx, id, updates, actor); err != nil {
if strings.Contains(err.Error(), "failed to update issue") {
var retriable = errors.Is(err, context.DeadlineExceeded) || isConnectionError(err)
if retriable { /* reopen tx and retry */ }
}
return err
} Prevention
- Ensure update values match column types and constraints.
- Keep transactions short to avoid locks/timeouts.
- Handle transient DB errors (Dolt restarts, dropped conns) with bounded retries.
- Run schema checks (bd doctor) after version upgrades to catch drift.
When it happens
Trigger: tx.ExecContext fails: constraint violations (e.g. NOT NULL, CHECK on status), value type mismatches with the column schema, table lock/timeout, connection loss, or an invalid value for a newly-allowed field not matching the schema.
Common situations: Setting a column to a type the driver can't bind; Dolt server restart or dropped connection mid-transaction; schema drift where a column is stricter than the code expects; unique/PK conflicts via dependent tables.
Related errors
- failed to begin transaction: %w
- failed to commit is_blocked repairs: %w
- resolve existing wisps for batch delete: %w
- resolve custom issue types: %w
- set repo mtime: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/552328126cb3a8f0.
Report an issue: GitHub.