{"record":{"id":"4ed78b4fe6156b3d","repo":"crowdsecurity/crowdsec","slug":"nil-alert-builder-at-index-d","errorCode":null,"errorMessage":"nil alert builder at index %d","messagePattern":"nil alert builder at index (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/database/alerts.go","lineNumber":595,"sourceCode":"\t\t}\n\n\t\treturn err\n\t}\n\n\treturn fmt.Errorf(\"exceeded %d busy retries\", maxLockRetries)\n}\n\nfunc (c *Client) saveAlerts(ctx context.Context, client *ent.Client, batch []alertCreatePlan) ([]string, error) {\n\tif len(batch) == 0 {\n\t\tlog.Warningf(\"no alerts to create, discarded?\")\n\t\treturn nil, nil\n\t}\n\n\t// extract builders in the same order\n\tbuilders := make([]*ent.AlertCreate, len(batch))\n\tfor i := range batch {\n\t\tif batch[i].builder == nil {\n\t\t\treturn nil, fmt.Errorf(\"nil alert builder at index %d\", i)\n\t\t}\n\n\t\tbuilders[i] = batch[i].builder\n\t}\n\n\talertsCreateBulk, err := client.Alert.CreateBulk(builders...).Save(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"bulk creating alert: %w: %w\", err, BulkError)\n\t}\n\n\tret := make([]string, len(alertsCreateBulk))\n\tfor i, a := range alertsCreateBulk {\n\t\tret[i] = strconv.Itoa(a.ID)\n\n\t\td := batch[i].decisions\n\t\tif len(d) == 0 {\n\t\t\tcontinue\n\t\t}","sourceCodeStart":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/database/alerts.go#L577-L613","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nplan := alertCreatePlan{decisions: decisions}\nbatch = append(batch, plan)\n\n// after\nbuilder := txEnt.Alert.Create().SetScenario(*alertItem.Scenario).SetMessage(*alertItem.Message)\nbatch = append(batch, alertCreatePlan{builder: builder, decisions: decisions})","handlingStrategy":"type-guard","validationCode":"for i, plan := range plans {\n\tif plan.builder == nil {\n\t\treturn fmt.Errorf(\"plan %d has nil builder; skipping batch\", i)\n\t}\n}","typeGuard":"func validPlan(p alertCreatePlan) bool { return p.builder != nil }","tryCatchPattern":"ids, err := client.CreateAlert(ctx, machineID, alerts)\nif err != nil && strings.Contains(err.Error(), \"nil alert builder\") {\n\t// internal bug: log and report; no user-side fix\n\tlog.Errorf(\"crowdsec internal error: %v\", err)\n}","preventionTips":["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"],"tags":["database","internal-invariant","crowdsec","ent-orm"],"backgroundTag":"internal-invariant-violation","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"}