{"record":{"id":"a13addcf9bf3a2eb","repo":"gastownhall/beads","slug":"add-label-record-event-w","errorCode":null,"errorMessage":"add label: record event: %w","messagePattern":"add label: record event: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/labels.go","lineNumber":162,"sourceCode":"\t\tif labelTable == \"\" {\n\t\t\tlabelTable = lt\n\t\t}\n\t\tif eventTable == \"\" {\n\t\t\teventTable = et\n\t\t}\n\t}\n\t//nolint:gosec // G201: labelTable is from WispTableRouting (\"labels\" or \"wisp_labels\")\n\tif _, err := tx.ExecContext(ctx, fmt.Sprintf(`INSERT IGNORE INTO %s (issue_id, label) VALUES (?, ?)`, labelTable), issueID, label); err != nil {\n\t\treturn fmt.Errorf(\"add label: %w\", err)\n\t}\n\tcomment := \"Added label: \" + label\n\tif err := InsertDerivedEvent(ctx, tx, eventTable, AuxEvent{\n\t\tIssueID:   issueID,\n\t\tEventType: types.EventLabelAdded,\n\t\tActor:     actor,\n\t\tComment:   str(comment),\n\t}); err != nil {\n\t\treturn fmt.Errorf(\"add label: record event: %w\", err)\n\t}\n\t// A label is part of the bead snapshot, so a label write journals as an\n\t// update carrying the complete post-mutation set.\n\treturn RecordEventInTx(ctx, tx, EventUpdate, issueID, actor)\n}\n\n// RemoveLabelInTx removes a label from an issue and records an event within\n// an existing transaction. Automatically routes to wisp tables if the ID is\n// an active wisp.\n//\n//nolint:gosec // G201: table names come from WispTableRouting (hardcoded constants)\nfunc RemoveLabelInTx(ctx context.Context, tx DBTX, labelTable, eventTable, issueID, label, actor string) error {\n\tif labelTable == \"\" || eventTable == \"\" {\n\t\tisWisp := IsActiveWispInTx(ctx, tx, issueID)\n\t\t_, lt, et, _ := WispTableRouting(isWisp)\n\t\tif labelTable == \"\" {\n\t\t\tlabelTable = lt\n\t\t}","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/labels.go#L144-L180","documentation":"AddLabelInTx returns this when the label row inserted fine but recording the derived 'label added' aux event via InsertDerivedEvent fails. The label write and its event are meant to be atomic within the transaction, so a failed event recording aborts the operation and the caller's transaction will roll back the insert.","triggerScenarios":"ApplyLabelPatch → AddLabelInTx where InsertDerivedEvent fails: event table missing, event row violates constraints, or connection error after the label INSERT succeeded.","commonSituations":"Schema versions missing the derived-events table; oversized comment payloads; connection drops mid-transaction on remote backends.","solutions":["Run schema migrations to ensure the events table exists","Retry the whole transaction — the atomic design means partial state is rolled back","Inspect the wrapped InsertDerivedEvent error for constraint or connection specifics"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"var exists int\n_ = tx.QueryRow(`SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'events'`).Scan(&exists)\nif exists == 0 { return errors.New(\"events table missing; run bd migrate\") }","typeGuard":null,"tryCatchPattern":"if err := issueops.AddLabelInTx(ctx, tx, issueID, label, actor); err != nil {\n    if strings.Contains(err.Error(), \"record event\") {\n        // transaction will roll back the label insert; safe to retry whole tx\n    }\n    return err\n}","preventionTips":["Keep schema migrations current for derived-event tables","Retry whole transactions — atomicity means no partial label state persists","Keep event comment payloads small","Watch for mid-transaction connection errors on remote backends"],"tags":["database","events","labels","transaction"],"backgroundTag":"event-record-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}