gastownhall/beads · error
db: Update %s: clear lease: %w
Error message
db: Update %s: clear lease: %w
What it means
Wrapped failure from the lease-cleanup step: after a successful UPDATE, if ManageLeaseOnUpdate determined the lease must be cleared (and the wisps table is not in use), Update runs issueops.DeleteLeaseInTx. This error means that delete failed. The issue row itself is already updated; only the lease row remains uncleared, so the unit of work aborts with an inconsistent in-transaction state (the surrounding transaction should roll back both).
Source
Thrown at internal/storage/domain/db/issue.go:287
args = append(args, id)
//nolint:gosec // G201: table is one of two hardcoded constants
q := fmt.Sprintf("UPDATE %s SET %s WHERE id = ?", table, strings.Join(setClauses, ", "))
res, err := r.runner.ExecContext(ctx, q, args...)
if err != nil {
return fmt.Errorf("db: Update %s: %w", id, err)
}
rows, err := res.RowsAffected()
if err != nil {
return fmt.Errorf("db: Update %s: rows affected: %w", id, err)
}
if rows == 0 {
return fmt.Errorf("db: Update %s: %w", id, sql.ErrNoRows)
}
if clearLease && !opts.UseWispsTable {
if err := issueops.DeleteLeaseInTx(ctx, r.runner, id); err != nil {
return fmt.Errorf("db: Update %s: clear lease: %w", id, err)
}
}
// Event-type parity: embedded records EventClosed / EventReopened /
// EventStatusChanged for status transitions (issueops.DetermineEventType),
// EventUpdated otherwise.
eventType := types.EventUpdated
if statusChanging {
eventType = issueops.DetermineEventType(oldIssue, updates)
}
if err := r.events.Record(ctx, domain.Event{
IssueID: id,
Type: eventType,
Actor: actor,
}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {
return err
}
View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped inner error from DeleteLeaseInTx for the driver-level cause.
- Retry the whole unit of work; the transactional wrapper ensures the prior UPDATE is not partially committed.
- Inspect lease-row contention (concurrent Claim) and serialize conflicting operations.
- Verify lease table schema/permissions if the delete consistently fails.
Example fix
// before: ignoring partial failure
err := repo.Update(ctx, id, updates, actor, opts)
// after: redo the unit of work so lease cleanup re-runs
if err := repo.Update(ctx, id, updates, actor, opts); err != nil && strings.Contains(err.Error(), "clear lease") {
return redoUnitOfWork(ctx, id, updates)
} Defensive patterns
Strategy: retry
Validate before calling
// avoid forcing lease clear outside the wisps path unexpectedly
if clearLeaseIntended && opts.UseWispsTable { /* lease clearing is skipped — account for it */ } Try / catch
if err := repo.Update(ctx, id, updates, actor, opts); err != nil && strings.Contains(err.Error(), "clear lease") {
// transaction rolled back: redo the whole unit of work on a fresh session
return redoUnitOfWork(freshCtx, id, updates)
} Prevention
- Serialize claim/update operations to avoid lease-row contention
- Always redo the full unit of work — the prior UPDATE rolls back with the transaction
- Keep lease-table schema and permissions correct
- Monitor for repeated clear-lease failures as a sign of server or contention issues
When it happens
Trigger: Calling Update whose updates clear the lease/ownership (e.g. status or assignee change per ManageLeaseOnUpdate) where DeleteLeaseInTx's DELETE fails — connectivity loss, lock contention on the lease row, or driver error inside the same transaction.
Common situations: Concurrent claim/update racing on the lease row; server hiccup mid-transaction; foreign-key or permission problems on the lease table.
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/c06effd5cd72bcf9.
Report an issue: GitHub.