quarkusio/quarkus · error · UnsupportedOperationException

retrieving all roles not supported when JAX-RS security cont

Error message

retrieving all roles not supported when JAX-RS security context has been replaced

What it means

This UnsupportedOperationException is thrown by the synthetic SecurityContext that Quarkus REST (RESTEasy Reactive) installs when application code replaced the JAX-RS SecurityContext via a filter (SecurityContextOverrideHandler). Because the overriding code fully controls identity/role checks, the framework cannot enumerate all roles, so getRoles() is intentionally unsupported. Only hasRole(String) delegation to the replaced context is provided.

Source

Thrown at extensions/resteasy-reactive/rest/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/security/SecurityContextOverrideHandler.java:76

                @Override
                public SecurityIdentity apply(SecurityIdentity old) {
                    Set<Credential> oldCredentials = old.getCredentials();
                    Set<Permission> oldPermissions = old.getPermissions();
                    Map<String, Object> oldAttributes = old.getAttributes();
                    SecurityIdentity newIdentity = new SecurityIdentity() {
                        @Override
                        public Principal getPrincipal() {
                            return modified.getUserPrincipal();
                        }

                        @Override
                        public boolean isAnonymous() {
                            return modified.getUserPrincipal() == null;
                        }

                        @Override
                        public Set<String> getRoles() {
                            throw new UnsupportedOperationException(
                                    "retrieving all roles not supported when JAX-RS security context has been replaced");
                        }

                        @Override
                        public boolean hasRole(String role) {
                            return modified.isUserInRole(role);
                        }

                        @SuppressWarnings("unchecked")
                        @Override
                        public <T extends Credential> T getCredential(Class<T> credentialType) {
                            for (Credential cred : getCredentials()) {
                                if (credentialType.isAssignableFrom(cred.getClass())) {
                                    return (T) cred;
                                }
                            }
                            return null;
                        }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Do not call getRoles() on the replaced SecurityContext; use hasRole("role") for individual checks instead of enumerating roles
  2. If you need role enumeration, have your custom SecurityContext implement getRoles() yourself before installing it via setSecurityContext()
  3. Keep Quarkus-managed SecurityIdentity intact: use IdentityProvider / SecurityIdentityAugmentor to add roles instead of replacing the SecurityContext
  4. If you only replaced the context for a different principal, consider augmenting the existing SecurityIdentity rather than overriding the whole SecurityContext

Example fix

// before
SecurityContext sc = ctx.getSecurityContext();
Set<String> roles = sc.getRoles(); // throws UnsupportedOperationException
// after
boolean isAdmin = ctx.getSecurityContext().isUserInRole("admin");
// or better: augment identity instead of overriding context
public class RolesAugmentor implements SecurityIdentityAugmentor {
    public Uni<SecurityIdentity> augment(SecurityIdentity identity, SecurityIdentityAugmentationContext c) {
        return Uni.createFrom().item(identity); // add roles here
    }
}
Defensive patterns

Strategy: fallback

Validate before calling

SecurityContext sc = ctx.getSecurityContext();
Set<String> roles;
try {
    roles = sc.getRoles();
} catch (UnsupportedOperationException e) {
    roles = Set.of(); // or derive from SecurityIdentity augmentors
}
if (roles.isEmpty() && sc.getUserPrincipal() != null) {
    // use sc.isUserInRole(role) per expected role instead
}

Type guard

boolean supportsGetRoles(SecurityContext sc) {
    try { sc.getRoles(); return true; } catch (UnsupportedOperationException e) { return false; }
}

Try / catch

try {
    Set<String> roles = securityContext.getRoles();
} catch (UnsupportedOperationException e) {
    // fall back to isUserInRole checks or SecurityIdentity.getRoles()
}

Prevention

When it happens

Trigger: Calling SecurityContext.getRoles() (directly or via @RolesAllowed internal role gathering / SecurityIdentity.getRoles()) after a ContainerRequestFilter has called ContainerRequestContext.setSecurityContext() with a custom SecurityContext whose getRoles is not backed by a real role store.

Common situations: Custom auth filters that wrap or replace the security context; migrating code from classic RESTEasy where getRoles() worked; using quarkus-security APIs (e.g. isUserInRole-based annotations are fine, but role enumeration or programmatic SecurityIdentity.getRoles() fails); testing security filters that swap contexts.

Related errors


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