crowdsecurity/crowdsec · error · BulkError

error creating alert: %w: %w

Error message

error creating alert: %w: %w

What it means

c.Ent.Alert.Create()....Save(ctx) failed while persisting the alert row itself, before any decisions are written; the error is wrapped with the BulkError sentinel so callers can classify it as a bulk/insert-stage failure. The wrapped ent error contains the driver-level reason (constraint, connection, cancellation).

Source

Thrown at pkg/database/alerts.go:249

		SetSourceValue(*alertItem.Source.Value).
		SetSourceIp(alertItem.Source.IP).
		SetSourceRange(alertItem.Source.Range).
		SetSourceAsNumber(alertItem.Source.AsNumber).
		SetSourceAsName(alertItem.Source.AsName).
		SetSourceCountry(alertItem.Source.Cn).
		SetSourceLatitude(alertItem.Source.Latitude).
		SetSourceLongitude(alertItem.Source.Longitude).
		SetCapacity(*alertItem.Capacity).
		SetLeakSpeed(*alertItem.Leakspeed).
		SetSimulated(*alertItem.Simulated).
		SetScenarioVersion(*alertItem.ScenarioVersion).
		SetScenarioHash(*alertItem.ScenarioHash).
		SetKind(alertItem.Kind).
		SetRemediation(true) // it's from CAPI, we always have decisions

	alertRef, err := alertB.Save(ctx)
	if err != nil {
		return 0, 0, 0, fmt.Errorf("error creating alert: %w: %w", err, BulkError)
	}

	if len(alertItem.Decisions) == 0 {
		return alertRef.ID, 0, 0, nil
	}

	txClient, err := c.Ent.Tx(ctx)
	if err != nil {
		return 0, 0, 0, fmt.Errorf("error creating transaction: %w: %w", err, BulkError)
	}

	decOrigin := CapiMachineID

	if *alertItem.Decisions[0].Origin == CapiMachineID || *alertItem.Decisions[0].Origin == CapiListsMachineID {
		decOrigin = *alertItem.Decisions[0].Origin
	} else {
		log.Warningf("unexpected origin %s", *alertItem.Decisions[0].Origin)
	}

View on GitHub (pinned to 909b515798)

Solutions

  1. Read the wrapped error: a NOT NULL/constraint violation points to a nil or empty field in the incoming models.Alert — validate/populate it before SaveAlerts
  2. Check DB availability and schema version (cscli version, database migration status)
  3. If SQLite is locked, stop concurrent crowdsec/cscli instances or move to a client-server DB
  4. Retry on transient connection errors; the CAPI pull will re-deliver the alert

Example fix

// before
alertB := c.Ent.Alert.Create().SetCapacity(*alertItem.Capacity) // panics or fails if nil
// after
if alertItem.Capacity != nil {
    alertB = alertB.SetCapacity(*alertItem.Capacity)
} else {
    alertB = alertB.SetCapacity(0)
}
Defensive patterns

Strategy: validation

Validate before calling

// pre-validate all pointer fields the builder dereferences
required := map[string]*string{
    "scenario": alert.Scenario, "message": alert.Message,
    "capacity": alert.Capacity, "leakspeed": alert.Leakspeed,
    "scenario_version": alert.ScenarioVersion, "scenario_hash": alert.ScenarioHash,
}
for k, v := range required {
    if v == nil { return fmt.Errorf("alert missing %s", k) }
}
if alert.Source == nil || alert.Source.Scope == nil || alert.Source.Value == nil {
    return errors.New("alert missing source scope/value")
}

Type guard

func alertInsertable(a *models.Alert) bool {
    return a != nil && a.Scenario != nil && a.Message != nil && a.EventsCount != nil &&
        a.Source != nil && a.Source.Scope != nil && a.Source.Value != nil &&
        a.Capacity != nil && a.Leakspeed != nil && a.Simulated != nil &&
        a.ScenarioVersion != nil && a.ScenarioHash != nil
}

Try / catch

if _, _, _, err := db.UpdateCommunityBlocklist(ctx, alert); err != nil {
    if errors.Is(err, database.BulkError) || ent.IsConstraintError(err) {
        log.Warnf("dropping malformed alert: %v", err)
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling UpdateCommunityBlocklist (via SaveAlerts) when the alerts table insert fails: NOT NULL constraint on a field whose pointer in alertItem is nil (Scenario, Message, EventsCount, Source, Capacity, Leakspeed, Simulated, ScenarioVersion, ScenarioHash are all dereferenced without nil checks), or the database is down.

Common situations: A CAPI or custom list alert is missing optional-looking fields that the builder dereferences (e.g. nil Capacity or Source.Scope causes a panic or NOT NULL violation); SQLite file locked/corrupted; DB schema out of date after an upgrade (run cscli migrate / restart so migrations apply).

Related errors


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