{"record":{"id":"840d1d5796567004","repo":"quarkusio/quarkus","slug":"expression-expression-in-the-preauthorize-an","errorCode":null,"errorMessage":"Expression: '<expression>' in the @PreAuthorize annotation on method '<method>' of class '<class>' is malformed","messagePattern":"Expression: '<expression>' in the @PreAuthorize annotation on method '<method>' of class '<class>' is malformed","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions/spring-security/deployment/src/main/java/io/quarkus/spring/security/deployment/HasRoleValueUtil.java","lineNumber":37,"sourceCode":"\n    private static final String BEAN_FIELD_REGEX = \"@(\\\\w+)\\\\.(\\\\w+)\";\n    private static final Pattern BEAN_FIELD_PATTERN = Pattern.compile(BEAN_FIELD_REGEX);\n\n    private HasRoleValueUtil() {\n    }\n\n    static Supplier<String[]> getHasRoleValueProducer(String hasRoleValue, MethodInfo methodInfo,\n            IndexView index,\n            Map<String, DotName> springBeansNameToDotName,\n            Map<String, ClassInfo> springBeansNameToClassInfo,\n            Set<String> beansReferencedInPreAuthorized,\n            SpringSecurityRecorder recorder) {\n        if (hasRoleValue.startsWith(\"'\") && hasRoleValue.endsWith(\"'\")) {\n            return recorder.staticHasRole(hasRoleValue.replace(\"'\", \"\"));\n        } else if (hasRoleValue.startsWith(\"@\")) {\n            Matcher beanFieldMatcher = BEAN_FIELD_PATTERN.matcher(hasRoleValue);\n            if (!beanFieldMatcher.find()) {\n                throw SpringSecurityProcessorUtil.createGenericMalformedException(methodInfo, hasRoleValue);\n            }\n\n            String beanName = beanFieldMatcher.group(1);\n            ClassInfo beanClassInfo = SpringSecurityProcessorUtil.getClassInfoFromBeanName(beanName, index,\n                    springBeansNameToDotName, springBeansNameToClassInfo, hasRoleValue, methodInfo);\n\n            String fieldName = beanFieldMatcher.group(2);\n            FieldInfo fieldInfo = beanClassInfo.field(fieldName);\n            //TODO: detect normal scoped beans and throw an exception, as it will read the field from the proxy\n            if ((fieldInfo == null) || !Modifier.isPublic(fieldInfo.flags())\n                    || !DotNames.STRING.equals(fieldInfo.type().name())) {\n                throw new IllegalArgumentException(\"Bean named '\" + beanName + \"' found in expression '\" + hasRoleValue\n                        + \"' in the @PreAuthorize annotation on method \" + methodInfo.name() + \" of class \"\n                        + methodInfo.declaringClass() + \" does not have a public field named '\" + fieldName\n                        + \"' of type String\");\n            }\n\n            beansReferencedInPreAuthorized.add(fieldInfo.declaringClass().name().toString());","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/spring-security/deployment/src/main/java/io/quarkus/spring/security/deployment/HasRoleValueUtil.java#L19-L55","documentation":"HasRoleValueUtil parses the hasRole(...) argument of @PreAuthorize. Valid forms are a quoted literal ('ROLE_ADMIN') or a bean-field reference (@configBean.someField). If the expression is neither (e.g. starts with @ but does not match @bean.field, or is an unsupported form), SpringSecurityProcessorUtil.createGenericMalformedException throws this IllegalArgumentException at build time.","triggerScenarios":"getHasRoleValueProducer is given a hasRole value that is not wrapped in single quotes and does not match the regex @(\\w+)\\.(\\w+) — e.g. @beanName.method(), hasRole with double quotes, concatenation, or any other SpEL syntax.","commonSituations":"Copying full Spring SpEL expressions like hasRole(@resolver.role()) from a Spring Boot app; using double quotes instead of single quotes for the role literal; writing complex expressions the Quarkus Spring Security subset does not support.","solutions":["Wrap the role literal in single quotes: hasRole('ROLE_ADMIN')","If the role comes from configuration, use the @bean.field form: hasRole(@rolesConfig.adminRole) with a public String field","Simplify the expression — only literal or bean-field forms are supported; move complex logic into a bean method invoked as the whole @PreAuthorize expression"],"exampleFix":"// before\n@PreAuthorize(\"hasRole(@roleResolver.getRole())\")\n\n// after\n@PreAuthorize(\"hasRole(@rolesConfig.userRole)\") // public String userRole field on the config bean","handlingStrategy":"validation","validationCode":"String v = \"'ROLE_ADMIN'\"; // or \"@config.role\"\nboolean valid = (v.startsWith(\"'\") && v.endsWith(\"'\"))\n    || (v.startsWith(\"@\") && v.matches(\"@\\\\w+\\\\.\\\\w+\"));\nif (!valid) throw new IllegalArgumentException(\"Unsupported hasRole value: \" + v);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always single-quote role literals inside hasRole(...)","Use only the @bean.field form for dynamic role names","Consult the Quarkus Spring Security guide for the supported expression subset before porting Spring expressions"],"tags":["spring-security","build-time","preauthorize","malformed-expression"],"backgroundTag":"malformed-preauthorize-expression","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}