gastownhall/beads · error

add label: %w

Error message

add label: %w

What it means

AddLabelInTx returns this when the INSERT IGNORE of a label row into labels/wisp_labels fails at execution time. The library uses INSERT IGNORE so duplicate labels are idempotent; any error here is a real failure (constraint violation other than duplicates, missing table, connection error). An associated derived event and update journal entry are only written after this succeeds.

Source

Thrown at internal/storage/issueops/labels.go:153

	// Reject an over-length label up front. The INSERT IGNORE below would
	// otherwise silently truncate it to the VARCHAR(255) column, storing a label
	// the caller never sent; a typed ErrFieldTooLong is the clean rejection.
	if err := types.CheckFieldLen("label", label); err != nil {
		return err
	}
	if labelTable == "" || eventTable == "" {
		isWisp := IsActiveWispInTx(ctx, tx, issueID)
		_, lt, et, _ := WispTableRouting(isWisp)
		if labelTable == "" {
			labelTable = lt
		}
		if eventTable == "" {
			eventTable = et
		}
	}
	//nolint:gosec // G201: labelTable is from WispTableRouting ("labels" or "wisp_labels")
	if _, err := tx.ExecContext(ctx, fmt.Sprintf(`INSERT IGNORE INTO %s (issue_id, label) VALUES (?, ?)`, labelTable), issueID, label); err != nil {
		return fmt.Errorf("add label: %w", err)
	}
	comment := "Added label: " + label
	if err := InsertDerivedEvent(ctx, tx, eventTable, AuxEvent{
		IssueID:   issueID,
		EventType: types.EventLabelAdded,
		Actor:     actor,
		Comment:   str(comment),
	}); err != nil {
		return fmt.Errorf("add label: record event: %w", err)
	}
	// A label is part of the bead snapshot, so a label write journals as an
	// update carrying the complete post-mutation set.
	return RecordEventInTx(ctx, tx, EventUpdate, issueID, actor)
}

// 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.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the issue exists before adding the label
  2. Run schema migrations to ensure label tables exist
  3. Check the wrapped driver error for FK/constraint messages and correct the input data
Defensive patterns

Strategy: validation

Validate before calling

var n int
if err := tx.QueryRow(`SELECT COUNT(*) FROM issues WHERE id = ?`, issueID).Scan(&n); err != nil || n == 0 {
    return fmt.Errorf("issue %s does not exist; cannot add label", issueID)
}

Try / catch

if err := issueops.AddLabelInTx(ctx, tx, issueID, label, actor); err != nil {
    if strings.Contains(err.Error(), "foreign key") || strings.Contains(err.Error(), "Duplicate") {
        // invalid issue or constraint issue: fix input, don't retry blindly
    }
    return err
}

Prevention

When it happens

Trigger: Calling AddLabelInTx (via ApplyLabelPatch) when the INSERT fails: label table missing, foreign-key violation because the issue doesn't exist, or connection failure within the transaction.

Common situations: Adding a label to an already-deleted issue ID (FK violation); schema version missing wisp_labels; transaction aborted earlier by a prior error leaving the connection in a bad state.

Related errors


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