crowdsecurity/crowdsec · error

event has multiple source types : %s != %s

Error message

event has multiple source types : %s != %s

What it means

During alert formatting, crowdsec requires every event in the overflow queue to share one source scope type (e.g. all Ip, all Range). When processing queued events, if a second source's Scope differs from the first one recorded, alertFormatSource refuses to build a mixed-type alert with this error. It is an internal consistency check: a single alert cannot represent events scoped to different source types.

Source

Thrown at pkg/leakybucket/overflows.go:287

	sources := make(map[string]models.Source)

	log.Debugf("Formatting (%s) - scope Info : scope_type:%s / scope_filter:%s", leaky.Factory.Spec.Name, leaky.Factory.Spec.ScopeType.Scope, leaky.Factory.Spec.ScopeType.Filter)

	qEvents := queue.GetQueue()
	for idx := range qEvents {
		srcs, err := SourceFromEvent(qEvents[idx], leaky)
		if err != nil {
			return nil, "", fmt.Errorf("while extracting scope from bucket %s: %w", leaky.Factory.Spec.Name, err)
		}

		for key, src := range srcs {
			if source_type == types.Undefined {
				source_type = *src.Scope
			}

			if *src.Scope != source_type {
				return nil, "",
					fmt.Errorf("event has multiple source types : %s != %s", *src.Scope, source_type)
			}

			sources[key] = src
		}
	}

	return sources, source_type, nil
}

// NewAlert will generate a RuntimeAlert and its APIAlert(s) from a bucket that overflowed
func NewAlert(leaky *Leaky, queue *pipeline.Queue) (pipeline.RuntimeAlert, error) {
	var runtimeAlert pipeline.RuntimeAlert

	leaky.logger.Tracef("Overflow (start: %s, end: %s)", leaky.First_ts, leaky.Ovflw_ts)
	/*
		Craft the models.Alert that is going to be duplicated for each source
	*/
	start_at, err := leaky.First_ts.MarshalText()

View on GitHub (pinned to 909b515798)

Solutions

  1. Check the scenario's scope filter for inputs that can be empty for some events and normalize them (e.g. fall back to evt.Parsed.source_ip)
  2. Ensure parsers/enrichers populate the fields the scope filter uses for every acquisition source the scenario applies to
  3. If the scenario is intentionally generic, split it so each log type has its own consistent scope
  4. Capture a dump of the queued events (leaky.Factory.Spec.Debug=true) to see which event produced the divergent scope

Example fix

// before
scope:
  type: range
  filter: evt.Enriched.target_ips
// after (guarantee a non-empty string)
scope:
  type: range
  filter: Get(evt.Enriched, 'target_ips', evt.Parsed.target_ip)
Defensive patterns

Strategy: validation

Validate before calling

// Ensure scope filters yield a consistent, non-empty string for all events:
// in the expression: Get(field, fallback) — never a bare optional field

Type guard

// Expression-level guard:
// val := Get(evt.Enriched, 'target_ips', evt.Parsed.target_ip); val != ''

Try / catch

// If generating alerts yourself:
alert, _, err := alertFormatSource(leaky, queue)
if err != nil && strings.Contains(err.Error(), "multiple source types") {
    log.Warnf("mixed-scope overflow in %s; fix the scope filter", leaky.Factory.Spec.Name)
}

Prevention

When it happens

Trigger: Bucket overflow where events in the same queue produce srcs whose *src.Scope values differ (e.g. some events yield types.Ip, others types.Range or types.Undefined) — typically when a scope filter returns differently-typed/empty values across events.

Common situations: Custom scope filters that return an empty string or non-string value for some events (which falls back to an undefined scope) while others resolve to an IP/Range; scenarios applied to heterogeneous acquisition sources.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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