{"record":{"id":"b5a0cc32e3459433","repo":"crowdsecurity/crowdsec","slug":"unable-to-create-alert-w","errorCode":null,"errorMessage":"unable to create alert: %w","messagePattern":"unable to create alert: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/alerts.go","lineNumber":59,"sourceCode":"\n// CreateOrUpdateAlert is specific to PAPI : It checks if alert already exists, otherwise inserts it\n// if alert already exists, it checks it associated decisions already exists\n// if some associated decisions are missing (ie. previous insert ended up in error) it inserts them\nfunc (c *Client) CreateOrUpdateAlert(ctx context.Context, machineID string, alertItem *models.Alert) (string, error) {\n\tif alertItem.UUID == \"\" {\n\t\treturn \"\", errors.New(\"alert UUID is empty\")\n\t}\n\n\talerts, err := c.Ent.Alert.Query().Where(alert.UUID(alertItem.UUID)).WithDecisions().All(ctx)\n\tif err != nil && !ent.IsNotFound(err) {\n\t\treturn \"\", fmt.Errorf(\"unable to query alerts for uuid %s: %w\", alertItem.UUID, err)\n\t}\n\n\t// alert wasn't found, insert it (expected hotpath)\n\tif ent.IsNotFound(err) || len(alerts) == 0 {\n\t\talertIDs, err := c.CreateAlert(ctx, machineID, []*models.Alert{alertItem})\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"unable to create alert: %w\", err)\n\t\t}\n\n\t\t// happy nilaway\n\t\tif len(alertIDs) == 0 {\n\t\t\treturn \"\", fmt.Errorf(\"unable to create alert: no IDs returned for alert %s\", alertItem.UUID)\n\t\t}\n\n\t\treturn alertIDs[0], nil\n\t}\n\n\t// this should never happen\n\tif len(alerts) > 1 {\n\t\treturn \"\", fmt.Errorf(\"multiple alerts found for uuid %s\", alertItem.UUID)\n\t}\n\n\tlog.Infof(\"Alert %s already exists, checking associated decisions\", alertItem.UUID)\n\n\t// alert is found, check for any missing decisions","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/alerts.go#L41-L77","documentation":"After the lookup shows the alert is absent, CreateOrUpdateAlert calls CreateAlert to insert it (with its events and decisions, in bulk transactions). If CreateAlert fails for any reason, the error is wrapped as 'unable to create alert: %w'. The underlying cause is usually a transactional insert failure surfaced through rollbackOnError (error 890).","triggerScenarios":"c.CreateAlert(ctx, machineID, []*models.Alert{alertItem}) returns an error during a PAPI alert push — typically a failed bulk insert (DB locked, constraint violation, oversized payload, malformed decision source range) — while the alert UUID was confirmed not already present.","commonSituations":"Concurrent LAPI writes locking SQLite; alert payload with invalid IP/range strings in decisions; decisions referencing malformed metadata; disk full; version mismatch causing schema mismatch during insert.","solutions":["Unwrap the error to identify the CreateAlert cause (lock, constraint, validation)","For SQLite lock errors, stagger writers or migrate to PostgreSQL/MySQL","Validate the alert payload (decision UUIDs, scope/value fields) before pushing","Retry: the lookup-by-UUID makes the operation idempotent, so a retry after a transient failure is safe"],"exampleFix":"// before: fire-and-forget push\n// after: retry transient insert failures\nid, err := client.CreateOrUpdateAlert(ctx, machineID, alert)\nif err != nil && isTransient(err) {\n    time.Sleep(backoff)\n    id, err = client.CreateOrUpdateAlert(ctx, machineID, alert)\n}","handlingStrategy":"retry","validationCode":"// validate payload before insert\nfor _, d := range alertItem.Decisions {\n    if d.UUID == \"\" || d.Value == nil || *d.Value == \"\" {\n        return errors.New(\"invalid decision in alert payload\")\n    }\n}\nif alertItem.UUID == \"\" { return errors.New(\"alert UUID is empty\") }","typeGuard":null,"tryCatchPattern":"id, err := client.CreateOrUpdateAlert(ctx, machineID, alert)\nif err != nil && strings.HasPrefix(err.Error(), \"unable to create alert:\") && !strings.Contains(err.Error(), \"no IDs\") {\n    time.Sleep(retryBackoff)\n    id, err = client.CreateOrUpdateAlert(ctx, machineID, alert) // idempotent by UUID\n}","preventionTips":["Validate decisions (UUID, type, value, duration) before pushing to LAPI","Don't send duplicate alerts concurrently — dedupe by UUID client-side","Use a server DB backend under concurrent load","Watch for SQLite lock logs and address contention early"],"tags":["database","insert","crowdsec","transaction"],"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-14T00:17:10.932Z"}