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

  1. Inspect the code path that constructs alertCreatePlan values and ensure txEnt.Alert.Create() is called for every appended plan before saveAlerts
  2. Check for recent refactors/patches to createAlertBatch or saveAlerts that may append empty plans; fix them to skip the append instead
  3. 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

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


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/4ed78b4fe6156b3d. Report an issue: GitHub.