quarkusio/quarkus · error · IllegalStateException
Function for trigger '${trigger}' has multiple matching invo
Error message
Function for trigger '${trigger}' has multiple matching invokers What it means
KnativeEventsBindingRecorder.init registers invokers keyed by trigger (ce-type) plus optional attribute filters. If two invokers for the same trigger have overlapping filters so a single incoming event could match more than one function, registration fails with IllegalStateException to prevent ambiguous dispatch.
Source
Thrown at extensions/funqy/funqy-knative-events/runtime/src/main/java/io/quarkus/funqy/runtime/bindings/knative/events/KnativeEventsBindingRecorder.java:106
trigger = annotation.trigger();
} else {
trigger = invoker.getName();
}
filter = filter(invoker.getName(), annotation);
} else {
trigger = invoker.getName();
filter = Collections.emptyList();
}
invokersFilters.put(invoker.getName(), filter);
typeTriggers.compute(trigger, (k, v) -> {
if (v == null) {
v = new ArrayList<>();
}
// validate if there are no conflicts for the same type (trigger) and defined filters
// as resolution based on trigger (ce-type) and optional filters (on ce-attributes) can return only
// one function invoker
if (v.stream().anyMatch(i -> hasSameFilters(i.getName(), invokersFilters.get(i.getName()), filter))) {
throw new IllegalStateException("Function for trigger '" + trigger + "' has multiple matching invokers");
}
v.add(invoker);
return v;
});
if (invoker.hasInput()) {
Type inputType = invoker.getInputType();
if (CloudEvent.class.equals(Reflections.getRawType(inputType))) {
if (inputType instanceof ParameterizedType) {
Type[] params = ((ParameterizedType) inputType).getActualTypeArguments();
if (params.length == 1) {
inputType = params[0];
invoker.getBindingContext().put(INPUT_CE_DATA_TYPE, inputType);
}
} else {
throw new RuntimeException("When using CloudEvent<> generic parameter must be used.");View on GitHub (pinned to e1c734241f)
Solutions
- Remove or rename the duplicate function's trigger type so each trigger maps to one invoker.
- Differentiate the functions' ce-attribute filters so they are mutually exclusive.
- Ensure only one bean exposes a given trigger; delete the stale duplicate class.
- Use quarkus.funqy.export to select a single function if multiple must exist in code.
Example fix
// before
@OnTrigger(type = "com.example.created") public void f1(String s) {}
@OnTrigger(type = "com.example.created") public void f2(String s) {}
// after
@OnTrigger(type = "com.example.created") public void f1(String s) {}
@OnTrigger(type = "com.example.updated") public void f2(String s) {} Defensive patterns
Strategy: validation
Validate before calling
// At startup, verify no two functions share the same trigger type
Map<String, Long> counts = functions.stream()
.collect(Collectors.groupingBy(f -> f.triggerType(), Collectors.counting()));
List<String> dupes = counts.entrySet().stream().filter(e -> e.getValue() > 1).map(Map.Entry::getKey).toList();
if (!dupes.isEmpty()) throw new IllegalStateException("Duplicate triggers: " + dupes); Type guard
boolean triggerIsUnique(String trigger, List<FunctionDef> all, FunctionDef self) {
return all.stream().filter(f -> f.triggerType().equals(trigger)).count() == 1;
} Try / catch
try { app.start(); } catch (IllegalStateException e) { if (e.getMessage().contains("multiple matching invokers")) { log.error("Duplicate trigger/filters on functions - disambiguate ce-type or filters", e); } throw e; } Prevention
- Keep one function per ce-type
- Make attribute filters mutually exclusive when sharing a type
- Name triggers with a naming convention to avoid collisions
- Review @OnTrigger/@Function annotations after copy-paste refactors
When it happens
Trigger: Two Funqy functions annotated with the same Knative trigger ce-type and identical/overlapping ce-attribute filters are registered during staticInit; hasSameFilters finds a conflict and init throws.
Common situations: Copy-pasting a @Function with the same @OnTrigger/@OnEvent type and filters; two deployments of the same function class; same ce-type intentionally used with filters that end up identical instead of disjoint.
Related errors
- Only supported major versions are 0 and 1.
- Don't know how to get event data (dataContentType: '%s', jav
- Cannot deserialize data for data-content-encoding: '${dataCo
- Don't know how to get event data (dataContentType: '%s', jav
- When using CloudEvent<> generic parameter must be used.
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/e7c64af1a791bf81.
Report an issue: GitHub.