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

  1. Retry the transaction; DELETE failures here are often transient lock contention
  2. Run schema migrations to ensure label tables exist
  3. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/b27e0fc2c25ce967. Report an issue: GitHub.