crowdsecurity/crowdsec · error

multiple alerts found for uuid %s

Error message

multiple alerts found for uuid %s

What it means

CreateOrUpdateAlert looks up alerts by UUID, which is expected to be unique. If more than one alert row matches the same UUID, it returns 'multiple alerts found for uuid %s' instead of guessing which one to update (commented 'this should never happen'). It signals a uniqueness violation in the alerts table.

Source

Thrown at pkg/database/alerts.go:72

	// alert wasn't found, insert it (expected hotpath)
	if ent.IsNotFound(err) || len(alerts) == 0 {
		alertIDs, err := c.CreateAlert(ctx, machineID, []*models.Alert{alertItem})
		if err != nil {
			return "", fmt.Errorf("unable to create alert: %w", err)
		}

		// happy nilaway
		if len(alertIDs) == 0 {
			return "", fmt.Errorf("unable to create alert: no IDs returned for alert %s", alertItem.UUID)
		}

		return alertIDs[0], nil
	}

	// this should never happen
	if len(alerts) > 1 {
		return "", fmt.Errorf("multiple alerts found for uuid %s", alertItem.UUID)
	}

	log.Infof("Alert %s already exists, checking associated decisions", alertItem.UUID)

	// alert is found, check for any missing decisions

	newUuids := make([]string, len(alertItem.Decisions))
	for i, decItem := range alertItem.Decisions {
		newUuids[i] = decItem.UUID
	}

	foundAlert := alerts[0]
	foundUuids := make([]string, len(foundAlert.Edges.Decisions))

	for i, decItem := range foundAlert.Edges.Decisions {
		foundUuids[i] = decItem.UUID
	}

View on GitHub (pinned to 909b515798)

Solutions

  1. Inspect duplicates: SELECT id, uuid FROM alerts WHERE uuid = '<uuid>'; on the crowdsec DB
  2. Delete the extra duplicate rows keeping one (backup the DB first: cp crowdsec.db crowdsec.db.bak)
  3. If duplicates are widespread, consider dumping alerts ('cscli alerts export' if available) and pruning with 'cscli alerts delete --all' to rebuild cleanly
  4. Upgrade crowdsec to a version enforcing alert UUID uniqueness

Example fix

// sqlite cleanup (backup first!)
// before: two rows share the uuid
// after: keep the oldest row
DELETE FROM alerts
WHERE uuid = 'xxxxxxxx-...' AND id NOT IN (
  SELECT MIN(id) FROM alerts WHERE uuid = 'xxxxxxxx-...'
);
Defensive patterns

Strategy: validation

Validate before calling

// detect duplicate UUIDs in the DB before pushing updates
rows, err := db.Query("SELECT COUNT(*) FROM alerts WHERE uuid = ?", alertUUID)
if err == nil && rows.Next() {
    var n int; rows.Scan(&n)
    if n > 1 { return errors.New("duplicate alert UUIDs in database; deduplicate first") }
}

Try / catch

id, err := client.CreateOrUpdateAlert(ctx, machineID, alert)
if err != nil && strings.HasPrefix(err.Error(), "multiple alerts found for uuid") {
    // stop retrying — requires DB-level dedup, not a retry
    return ErrCorruptAlertTable
}

Prevention

When it happens

Trigger: The SELECT alert.UUID(alertItem.UUID) returns len(alerts) > 1 — i.e., duplicate rows with the same UUID exist in the database, typically from a historical bug, manual DB manipulation, or an import that bypassed the unique constraint.

Common situations: Databases populated by old crowdsec versions before strict uniqueness, bulk imports from CAPI sync gone wrong, or hand-edited SQLite files. Rare in practice.

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/6d7f15df889bf69a. Report an issue: GitHub.