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

  1. Remove or rename the duplicate function's trigger type so each trigger maps to one invoker.
  2. Differentiate the functions' ce-attribute filters so they are mutually exclusive.
  3. Ensure only one bean exposes a given trigger; delete the stale duplicate class.
  4. 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

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


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/e7c64af1a791bf81. Report an issue: GitHub.