{"record":{"id":"4138b253eb1dbd40","repo":"quarkusio/quarkus","slug":"unable-to-determine-if-the-unsecuredmethod-me","errorCode":null,"errorMessage":"Unable to determine if the '${unsecuredMethod}' method should inherit security annotation of the '${securedMethod}' method (generics not supported yet)","messagePattern":"Unable to determine if the '(.+?)' method should inherit security annotation of the '(.+?)' method \\(generics not supported yet\\)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"extensions/security/spi/src/main/java/io/quarkus/security/spi/SecurityTransformerBuildItem.java","lineNumber":232,"sourceCode":"                && securedMethod.parametersCount() == unsecuredMethod.parametersCount()) {\n            if (securedMethod.parametersCount() == 0) {\n                return true;\n            }\n            var securedMethodParams = securedMethod.parameterTypes();\n            if (hasNoTypeVariable(securedMethodParams)) {\n                // no type variables\n                return securedMethodParams.equals(unsecuredMethod.parameterTypes());\n            } else {\n                // methods with type variables are currently not supported\n                throw newUnableToDetermineIfSecuredException(securedMethod, unsecuredMethod);\n            }\n        }\n        return false;\n    }\n\n    private static RuntimeException newUnableToDetermineIfSecuredException(MethodInfo securedMethod,\n            MethodInfo unsecuredMethod) {\n        throw new RuntimeException(\n                \"Unable to determine if the '%s' method should inherit security annotation of the '%s' method (generics not supported yet)\"\n                        .formatted(toString(unsecuredMethod), toString(securedMethod)));\n    }\n\n    private static boolean hasNoTypeVariable(List<Type> method) {\n        return method.stream().noneMatch(t -> {\n            if (t.kind() == TYPE_VARIABLE || t.kind() == Type.Kind.TYPE_VARIABLE_REFERENCE\n                    || t.kind() == Type.Kind.UNRESOLVED_TYPE_VARIABLE || t.kind() == Type.Kind.WILDCARD_TYPE) {\n                return true;\n            }\n            return t.kind() == PARAMETERIZED_TYPE && !hasNoTypeVariable(t.asParameterizedType().arguments());\n        });\n    }\n\n    private static Collection<ClassInfo> collectParentInterfaces(HashSet<ClassInfo> possiblySecuredInterfaces,\n            Set<DotName> securedInterfaceAnnotations, IndexView index) {\n        // this should avoid something like jakarta.data.repository.DataRepository\n        // because we only need inspect classes added by user for security annotations","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/security/spi/src/main/java/io/quarkus/security/spi/SecurityTransformerBuildItem.java#L214-L250","documentation":"When a class implements an interface whose method carries a security annotation, Quarkus checks whether the implementing method should inherit that annotation. If the methods involve generics/type variables, the Jandex-based check cannot decide safely and throws RuntimeException rather than silently skipping the check.","triggerScenarios":"A class implements/extends an interface with a @RolesAllowed/@Authenticated-annotated method where the implementation or interface method uses generic type parameters or type variables, defeating the hasNoTypeVariable checks during isImplementingSecuredMethod.","commonSituations":"Generic repositories/services like interface Repo<T> { @RolesAllowed(\"admin\") T find(long id); } implemented by a CDI bean; frameworks generating generic implementations; upgrading Quarkus surfaces the previously-unsound generics case.","solutions":["Re-annotate the implementing method directly with the security annotation instead of relying on inheritance from the generic interface method.","Remove type variables from the method signature (use concrete types) so the inheritance check can succeed.","Wrap the annotated generic method in a non-generic delegating bean that carries the security annotation.","Avoid securing the interface method at all; secure the implementation."],"exampleFix":"// before\ninterface Repo<T> { @RolesAllowed(\"admin\") T find(long id); }\nclass UserRepo implements Repo<User> { public User find(long id) {...} }\n\n// after\nclass UserRepo implements Repo<User> {\n    @RolesAllowed(\"admin\")\n    public User find(long id) {...}\n}","handlingStrategy":"validation","validationCode":"boolean genericsFree(java.lang.reflect.Method m) {\n    return m.getTypeParameters().length == 0\n        && java.util.Arrays.stream(m.getGenericParameterTypes()).noneMatch(t -> t instanceof java.lang.reflect.TypeVariable)\n        && !(m.getGenericReturnType() instanceof java.lang.reflect.TypeVariable);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Put security annotations on concrete implementing methods, not generic interface methods","Avoid type variables in secured method signatures","If generics are needed, add a non-generic secured delegate method"],"tags":["security","cdi","generics","build-time"],"backgroundTag":"security-annotation-inheritance-unsupported","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"}