crowdsecurity/crowdsec · error
attach decisions to alert %d: %w
Error message
attach decisions to alert %d: %w
What it means
After the alerts are bulk-inserted, saveAlerts attaches each alert's pre-created decision rows via Alert.Update().AddDecisions(), batched and retried on SQLite busy. This error wraps any failure of that update: the alert row exists (a.ID is printed) but the decision-linking write failed, causing the whole transaction to roll back.
Source
Thrown at pkg/database/alerts.go:621
return nil, fmt.Errorf("bulk creating alert: %w: %w", err, BulkError)
}
ret := make([]string, len(alertsCreateBulk))
for i, a := range alertsCreateBulk {
ret[i] = strconv.Itoa(a.ID)
d := batch[i].decisions
if len(d) == 0 {
continue
}
if err := slicetools.Batch(ctx, d, c.decisionBulkSize, func(ctx context.Context, d2 []*ent.Decision) error {
return retryOnBusy(func() error {
_, err := client.Alert.Update().Where(alert.IDEQ(a.ID)).AddDecisions(d2...).Save(ctx)
return err
})
}); err != nil {
return nil, fmt.Errorf("attach decisions to alert %d: %w", a.ID, err)
}
}
return ret, nil
}
type alertCreatePlan struct {
builder *ent.AlertCreate
decisions []*ent.Decision
}
func (c *Client) createAlertBatch(ctx context.Context, machineID string, owner *ent.Machine, alerts []*models.Alert) ([]string, error) {
tx, err := c.Ent.Tx(ctx)
if err != nil {
return nil, fmt.Errorf("creating alert transaction: %w: %w", err, BulkError)
}
txEnt := tx.Client()View on GitHub (pinned to 909b515798)
Solutions
- Check the wrapped error: if it is 'exceeded N busy retries', reduce concurrent writers on the SQLite DB or switch to MySQL/Postgres for the crowdsec database
- Retry the whole CreateAlert call — the transaction rolled back, so alerts were not persisted and it is safe to resubmit
- Increase throughput headroom: enable WAL mode for SQLite, or ensure the DB file is on local disk, not a network mount
- If it is a constraint error on decisions, inspect the decision payloads for duplicate IDs or invalid references before submitting
- Verify DB connectivity and timeouts in the database config if on a network backend
Example fix
// before
ids, err := client.CreateAlert(ctx, machineID, alerts)
if err != nil {
return fmt.Errorf("create failed: %w", err)
}
// after
ids, err := client.CreateAlert(ctx, machineID, alerts)
if err != nil {
if strings.Contains(err.Error(), "busy retries") {
time.Sleep(2 * time.Second)
ids, err = client.CreateAlert(ctx, machineID, alerts) // tx rolled back: safe retry
}
if err != nil {
return fmt.Errorf("create failed: %w", err)
}
} Defensive patterns
Strategy: retry
Validate before calling
// bound the work: cap decisions per alert before submission
if len(alert.Decisions) > maxDecisionsPerAlert {
return fmt.Errorf("alert %s has too many decisions (%d)", *alert.UUID, len(alert.Decisions))
} Type guard
null
Try / catch
ids, err := client.CreateAlert(ctx, machineID, alerts)
if err != nil && strings.Contains(err.Error(), "attach decisions") {
// tx rolled back; back off and retry whole batch
time.Sleep(backoff)
ids, err = client.CreateAlert(ctx, machineID, alerts)
} Prevention
- Reduce concurrent writers on SQLite (bouncers/cscli/LAPI) or switch to MySQL/Postgres
- Enable WAL mode on the SQLite database
- Retry idempotently: the transaction rolls back on this error, so resubmission is safe
- Keep alert decision counts reasonable; tune decisionBulkSize if batches are huge
- Watch for 'exceeded N busy retries' in crowdsec logs as an early sign of lock contention
When it happens
Trigger: The AddDecisions update fails for any reason: SQLite 'database is locked' persisting beyond maxLockRetries ("exceeded N busy retries"), a foreign-key/unique constraint on decision rows, the update statement hitting a dropped connection, or context cancellation during the batches.
Common situations: High-concurrency setups on SQLite where LAPI writes and cscli queries contend and the busy-retry loop is exhausted; very large alert batches with many decisions exceeding the configured decisionBulkSize across many batches while another writer holds the lock; DB connectivity blips on MySQL/Postgres backends.
Related errors
- no database configuration provided
- unable to update
- unable to delete
- object not found
- unable to parse time
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/6a655895e756115e.
Report an issue: GitHub.