crowdsecurity/crowdsec · error
nil alert builder at index %d
Error message
nil alert builder at index %d
What it means
saveAlerts iterates over the per-alert creation plans built by createAlertBatch and extracts each plan's ent.AlertCreate builder for the CreateBulk call. This error is an internal invariant check: a plan entry exists but its builder is nil, which should never happen since the builder is always constructed via txEnt.Alert.Create(). It guards against a programming error in batch construction, not a user or environment problem.
Source
Thrown at pkg/database/alerts.go:595
}
return err
}
return fmt.Errorf("exceeded %d busy retries", maxLockRetries)
}
func (c *Client) saveAlerts(ctx context.Context, client *ent.Client, batch []alertCreatePlan) ([]string, error) {
if len(batch) == 0 {
log.Warningf("no alerts to create, discarded?")
return nil, nil
}
// extract builders in the same order
builders := make([]*ent.AlertCreate, len(batch))
for i := range batch {
if batch[i].builder == nil {
return nil, fmt.Errorf("nil alert builder at index %d", i)
}
builders[i] = batch[i].builder
}
alertsCreateBulk, err := client.Alert.CreateBulk(builders...).Save(ctx)
if err != nil {
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
}View on GitHub (pinned to 909b515798)
Solutions
- Inspect the code path that constructs alertCreatePlan values and ensure txEnt.Alert.Create() is called for every appended plan before saveAlerts
- Check for recent refactors/patches to createAlertBatch or saveAlerts that may append empty plans; fix them to skip the append instead
- If hit in production, treat it as a bug: report it with the alert UUIDs being processed; the whole batch is rolled back so no data is lost
Example fix
// before
plan := alertCreatePlan{decisions: decisions}
batch = append(batch, plan)
// after
builder := txEnt.Alert.Create().SetScenario(*alertItem.Scenario).SetMessage(*alertItem.Message)
batch = append(batch, alertCreatePlan{builder: builder, decisions: decisions}) Defensive patterns
Strategy: type-guard
Validate before calling
for i, plan := range plans {
if plan.builder == nil {
return fmt.Errorf("plan %d has nil builder; skipping batch", i)
}
} Type guard
func validPlan(p alertCreatePlan) bool { return p.builder != nil } Try / catch
ids, err := client.CreateAlert(ctx, machineID, alerts)
if err != nil && strings.Contains(err.Error(), "nil alert builder") {
// internal bug: log and report; no user-side fix
log.Errorf("crowdsec internal error: %v", err)
} Prevention
- Always populate the builder field when constructing alertCreatePlan values
- Append the plan only after txEnt.Alert.Create() has run successfully
- Add a unit test constructing plans through the real code path before refactoring createAlertBatch
When it happens
Trigger: An alertCreatePlan entry was appended to the batch with a nil builder. With the current code in createAlertBatch this is unreachable; it could only occur if the batch-construction logic changes to conditionally skip builder creation while still appending a plan, or if an AlertCreate pointer is nil-ed out elsewhere before saveAlerts runs.
Common situations: Practically only seen after a code refactor or a plugin/patch that builds alertCreatePlan values manually and forgets to assign the builder, or when passing a zero-value alertCreatePlan struct (e.g. via a slice of partially initialized plans) into saveAlerts.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
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/4ed78b4fe6156b3d.
Report an issue: GitHub.