quarkusio/quarkus · error · IllegalArgumentException

Expression: '<expression>' in the @PreAuthorize annotation o

Error message

Expression: '<expression>' in the @PreAuthorize annotation on method '<method>' of class '<class>' is malformed

What it means

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.

Source

Thrown at extensions/spring-security/deployment/src/main/java/io/quarkus/spring/security/deployment/HasRoleValueUtil.java:37

    private static final String BEAN_FIELD_REGEX = "@(\\w+)\\.(\\w+)";
    private static final Pattern BEAN_FIELD_PATTERN = Pattern.compile(BEAN_FIELD_REGEX);

    private HasRoleValueUtil() {
    }

    static Supplier<String[]> getHasRoleValueProducer(String hasRoleValue, MethodInfo methodInfo,
            IndexView index,
            Map<String, DotName> springBeansNameToDotName,
            Map<String, ClassInfo> springBeansNameToClassInfo,
            Set<String> beansReferencedInPreAuthorized,
            SpringSecurityRecorder recorder) {
        if (hasRoleValue.startsWith("'") && hasRoleValue.endsWith("'")) {
            return recorder.staticHasRole(hasRoleValue.replace("'", ""));
        } else if (hasRoleValue.startsWith("@")) {
            Matcher beanFieldMatcher = BEAN_FIELD_PATTERN.matcher(hasRoleValue);
            if (!beanFieldMatcher.find()) {
                throw SpringSecurityProcessorUtil.createGenericMalformedException(methodInfo, hasRoleValue);
            }

            String beanName = beanFieldMatcher.group(1);
            ClassInfo beanClassInfo = SpringSecurityProcessorUtil.getClassInfoFromBeanName(beanName, index,
                    springBeansNameToDotName, springBeansNameToClassInfo, hasRoleValue, methodInfo);

            String fieldName = beanFieldMatcher.group(2);
            FieldInfo fieldInfo = beanClassInfo.field(fieldName);
            //TODO: detect normal scoped beans and throw an exception, as it will read the field from the proxy
            if ((fieldInfo == null) || !Modifier.isPublic(fieldInfo.flags())
                    || !DotNames.STRING.equals(fieldInfo.type().name())) {
                throw new IllegalArgumentException("Bean named '" + beanName + "' found in expression '" + hasRoleValue
                        + "' in the @PreAuthorize annotation on method " + methodInfo.name() + " of class "
                        + methodInfo.declaringClass() + " does not have a public field named '" + fieldName
                        + "' of type String");
            }

            beansReferencedInPreAuthorized.add(fieldInfo.declaringClass().name().toString());

View on GitHub (pinned to e1c734241f)

Solutions

  1. Wrap the role literal in single quotes: hasRole('ROLE_ADMIN')
  2. If the role comes from configuration, use the @bean.field form: hasRole(@rolesConfig.adminRole) with a public String field
  3. Simplify the expression — only literal or bean-field forms are supported; move complex logic into a bean method invoked as the whole @PreAuthorize expression

Example fix

// before
@PreAuthorize("hasRole(@roleResolver.getRole())")

// after
@PreAuthorize("hasRole(@rolesConfig.userRole)") // public String userRole field on the config bean
Defensive patterns

Strategy: validation

Validate before calling

String v = "'ROLE_ADMIN'"; // or "@config.role"
boolean valid = (v.startsWith("'") && v.endsWith("'"))
    || (v.startsWith("@") && v.matches("@\\w+\\.\\w+"));
if (!valid) throw new IllegalArgumentException("Unsupported hasRole value: " + v);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


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