gastownhall/beads · error
remove label: %w
Error message
remove label: %w
What it means
RemoveLabelInTx returns this when the DELETE of a label row from labels/wisp_labels fails at execution time. Like the add path, removal is followed by a derived event and update journal entry, all within the caller's transaction. The error wraps the raw driver error so the remove-label step is identifiable in ApplyLabelPatch failure chains.
Source
Thrown at internal/storage/issueops/labels.go:186
// RemoveLabelInTx removes a label from an issue and records an event within
// an existing transaction. Automatically routes to wisp tables if the ID is
// an active wisp.
//
//nolint:gosec // G201: table names come from WispTableRouting (hardcoded constants)
func RemoveLabelInTx(ctx context.Context, tx DBTX, labelTable, eventTable, issueID, label, actor string) error {
if labelTable == "" || eventTable == "" {
isWisp := IsActiveWispInTx(ctx, tx, issueID)
_, lt, et, _ := WispTableRouting(isWisp)
if labelTable == "" {
labelTable = lt
}
if eventTable == "" {
eventTable = et
}
}
if _, err := tx.ExecContext(ctx, fmt.Sprintf(`DELETE FROM %s WHERE issue_id = ? AND label = ?`, labelTable), issueID, label); err != nil {
return fmt.Errorf("remove label: %w", err)
}
comment := "Removed label: " + label
if err := InsertDerivedEvent(ctx, tx, eventTable, AuxEvent{
IssueID: issueID,
EventType: types.EventLabelRemoved,
Actor: actor,
Comment: str(comment),
}); err != nil {
return fmt.Errorf("remove label: record event: %w", err)
}
return RecordEventInTx(ctx, tx, EventUpdate, issueID, actor)
}
View on GitHub (pinned to 71377f2769)
Solutions
- Retry the transaction; DELETE failures here are often transient lock contention
- Run schema migrations to ensure label tables exist
- Inspect the wrapped error for deadlock/lock-wait messages and reduce transaction scope or retry with backoff
Defensive patterns
Strategy: retry
Validate before calling
// existence check is optional; removal is idempotent when the row is absent
if err := ctx.Err(); err != nil { return fmt.Errorf("context done before label removal: %w", err) } Try / catch
if err := issueops.RemoveLabelInTx(ctx, tx, issueID, label, actor); err != nil {
if strings.Contains(err.Error(), "deadlock") || strings.Contains(err.Error(), "lock wait") {
// transient contention: retry transaction with backoff
}
return err
} Prevention
- Retry transactions on deadlock/lock-wait errors with backoff
- Keep transactions short to reduce lock hold time
- Ensure schema migrations created the label tables
- Serialize label mutations for the same issue in high-contention workflows
When it happens
Trigger: ApplyLabelPatch → RemoveLabelInTx where the DELETE fails: label table missing, connection failure, or lock contention on the row inside a long transaction.
Common situations: Deadlocks/lock waits on busy beads under concurrent writers; schema versions missing wisp_labels; remote backend connection drops.
Related errors
- failed to remove federation peer: %w
- get labels from %s: %w
- db: LabelSQLRepository.Delete %s/%s: %w
- db: LabelSQLRepository.DeleteAllForIDs from %s: %w
- delete: drop labels: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b27e0fc2c25ce967.
Report an issue: GitHub.