{"record":{"id":"82ed8142ef73ee52","repo":"crowdsecurity/crowdsec","slug":"committing-alert-transaction-w-w","errorCode":null,"errorMessage":"committing alert transaction: %w: %w","messagePattern":"committing alert transaction: %w: %w","errorType":"exception","errorClass":"BulkError","httpStatus":null,"severity":"error","filePath":"pkg/database/alerts.go","lineNumber":718,"sourceCode":"\n\t\tif owner != nil {\n\t\t\tbuilder.SetOwner(owner)\n\t\t}\n\n\t\tbatch = append(batch, alertCreatePlan{\n\t\t\tbuilder:   builder,\n\t\t\tdecisions: decisions,\n\t\t})\n\t}\n\n\t// Save alerts, then attach decisions with retry logic\n\tids, err := c.saveAlerts(ctx, txEnt, batch)\n\tif err != nil {\n\t\treturn nil, rollbackOnError(tx, err, \"saving alerts\")\n\t}\n\n\tif err := tx.Commit(); err != nil {\n\t\treturn nil, fmt.Errorf(\"committing alert transaction: %w: %w\", err, BulkError)\n\t}\n\n\treturn ids, nil\n}\n\nfunc (c *Client) CreateAlert(ctx context.Context, machineID string, alertList []*models.Alert) ([]string, error) {\n\tvar (\n\t\towner *ent.Machine\n\t\terr   error\n\t)\n\n\tif machineID != \"\" {\n\t\towner, err = c.QueryMachineByID(ctx, machineID)\n\t\tif err != nil {\n\t\t\tif !errors.Is(err, UserNotExists) {\n\t\t\t\treturn nil, fmt.Errorf(\"machine '%s': %w\", machineID, err)\n\t\t\t}\n","sourceCodeStart":700,"sourceCodeEnd":736,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/alerts.go#L700-L736","documentation":"At the end of createAlertBatch, the transaction containing all alert inserts, events, metas, and decision links is committed. This error wraps a commit failure with the BulkError sentinel. All work succeeded at the statement level but the database refused to make it durable (e.g. commit-time lock conflict, connection loss, deferred constraint failure, disk I/O error).","triggerScenarios":"tx.Commit() returns an error: SQLite 'database is locked' at commit time under concurrent writers, connection dropped between the last statement and COMMIT on MySQL/Postgres, disk-full or I/O error while flushing, or a constraint deferred to commit time.","commonSituations":"SQLite contention when bouncers and cscli write simultaneously and the commit races with another writer's lock; MySQL 'server has gone away' due to wait_timeout on a long batch; disk full on the DB volume precisely at commit; network partition to a remote database during large batches.","solutions":["Read the wrapped driver error to distinguish lock, connection, or I/O failure","Simply retry CreateAlert — the commit rolled back, so the batch was never persisted and resubmission is safe","For SQLite lock errors, enable WAL journal mode and keep the DB on local disk; or migrate to MySQL/Postgres for high-concurrency setups","For connection errors, check DB server logs and network stability; adjust idle timeouts (e.g. MySQL wait_timeout) for long-running crowdsec processes","Free disk space / check I/O health on the database volume"],"exampleFix":"// before\nids, err := client.CreateAlert(ctx, machineID, alerts)\nif err != nil {\n\tpanic(err)\n}\n\n// after\nids, err := client.CreateAlert(ctx, machineID, alerts)\nif err != nil {\n\tif isRetryableDBError(err) { // lock/connection failures: tx rolled back, safe to retry\n\t\tids, err = client.CreateAlert(ctx, machineID, alerts)\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"alert creation failed: %w\", err)\n\t}\n}","handlingStrategy":"retry","validationCode":"if err := ctx.Err(); err != nil {\n\treturn fmt.Errorf(\"context cancelled before commit: %w\", err)\n}","typeGuard":"null","tryCatchPattern":"ids, err := client.CreateAlert(ctx, machineID, alerts)\nif err != nil && strings.Contains(err.Error(), \"committing alert transaction\") {\n\t// commit failed but tx rolled back; safe to retry after backoff\n\ttime.Sleep(backoff)\n\tids, err = client.CreateAlert(ctx, machineID, alerts)\n}","preventionTips":["Treat commit failure as safe-to-retry: nothing was persisted","Enable WAL mode for SQLite to reduce commit-time lock contention","Keep the database volume healthy: monitor disk space and I/O errors","On remote DBs, tune idle timeouts so long batches don't lose the connection at commit","Reduce write concurrency on SQLite or migrate to MySQL/Postgres for heavy setups"],"tags":["database","transaction-commit","sqlite","crowdsec"],"backgroundTag":"database-write-failed","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}