{"record":{"id":"eaf0dd6b941be59a","repo":"quarkusio/quarkus","slug":"the-method-method-is-using-keyed-but-the-a","errorCode":null,"errorMessage":"The method `${method}` is using `@Keyed` but the annotated parameter is not a `KeyedMulti`","messagePattern":"The method `(.+?)` is using `@Keyed` but the annotated parameter is not a `KeyedMulti`","errorType":"validation","errorClass":"ConfigurationException","httpStatus":null,"severity":"error","filePath":"extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/QuarkusMediatorConfigurationUtil.java","lineNumber":286,"sourceCode":"        }\n\n        return configuration;\n    }\n\n    private static void handleKeyedMulti(MethodInfo methodInfo,\n            RecorderContext recorderContext, QuarkusMediatorConfiguration configuration) {\n        if (methodInfo.parametersCount() == 1) { // @Keyed can only be used with a single parameter, a keyed multi\n            var info = methodInfo.parameters().get(0);\n            var annotation = info.annotation(ReactiveMessagingDotNames.KEYED);\n            if (annotation != null) {\n                // Make sure we have a keyed multi and an incoming.\n                if (methodInfo.annotation(INCOMING) == null && methodInfo.annotation(INCOMINGS) == null) {\n                    throw new ConfigurationException(\n                            \"The method `\" + methodInfo.name() + \"` is using `@Keyed` but is not annotated with `@Incoming`\");\n                }\n                if (info.type().kind() != Type.Kind.PARAMETERIZED_TYPE\n                        || !info.type().asParameterizedType().name().equals(ReactiveMessagingDotNames.KEYED_MULTI)) {\n                    throw new ConfigurationException(\"The method `\" + methodInfo.name()\n                            + \"` is using `@Keyed` but the annotated parameter is not a `KeyedMulti`\");\n                }\n                var extractor = (Class<? extends KeyValueExtractor>) recorderContext\n                        .classProxy(annotation.value().asClass().name().toString());\n                configuration.setKeyed(extractor);\n            }\n            if (info.type().kind() == Type.Kind.PARAMETERIZED_TYPE\n                    && info.type().asParameterizedType().name().equals(ReactiveMessagingDotNames.KEYED_MULTI)) {\n                var args = info.type().asParameterizedType().arguments();\n                configuration.setKeyType(recorderContext.classProxy(args.get(0).name().toString()));\n                configuration.setValueType(recorderContext.classProxy(args.get(1).name().toString()));\n            }\n        }\n    }\n\n    // TODO: avoid hard coding CompletionStage handling\n    private static Type determineReturnTypeOfSuspendMethod(MethodInfo methodInfo) {\n        Type lastParamType = methodInfo.parameterType(methodInfo.parametersCount() - 1);","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/smallrye-reactive-messaging/deployment/src/main/java/io/quarkus/smallrye/reactivemessaging/deployment/QuarkusMediatorConfigurationUtil.java#L268-L304","documentation":"@Keyed is only valid on a parameter whose type is a parameterized KeyedMulti<K, V>. At build time, handleKeyedMulti checks the annotated parameter's type; if it is any other type, the build fails with this ConfigurationException.","triggerScenarios":"Using @Keyed on a parameter typed Multi<V>, List<V>, a plain type, or a raw KeyedMulti without type parameters instead of KeyedMulti<String, Payload> (or another key type).","commonSituations":"Migrating from a Multi-based keyed pattern to KeyedMulti; forgetting type arguments on KeyedMulti; copy-pasting @Keyed onto the wrong parameter.","solutions":["Change the annotated parameter type to KeyedMulti<K, V> with explicit type parameters, e.g. KeyedMulti<String, Order>","Remove @Keyed if the stream is not a keyed multi","Ensure the first parameter (the @Keyed one) is the KeyedMulti itself per the API contract"],"exampleFix":"// before\nvoid process(@Keyed Multi<Order> orders) { ... }\n// after\n@Incoming(\"orders\")\nvoid process(@Keyed KeyedMulti<String, Order> orders) { ... }","handlingStrategy":"validation","validationCode":"Parameter p = method.getParameters()[0];\nif (p.isAnnotationPresent(Keyed.class)\n    && !(p.getType() instanceof ParameterizedType\n         && ((ParameterizedType) p.getType()).getRawType() == KeyedMulti.class)) {\n    throw new IllegalStateException(\"@Keyed parameter must be KeyedMulti<K,V>\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always declare KeyedMulti with explicit type parameters: KeyedMulti<String, Payload>","Keep the @Keyed annotation on the KeyedMulti parameter itself","Review parameter types when migrating from Multi-based keyed patterns"],"tags":["reactive-messaging","build-time-validation","generics"],"backgroundTag":"wrong-parameter-type","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}