quarkusio/quarkus · error · IllegalArgumentException
Policy has already been set
Error message
Policy has already been set
What it means
Thrown by HttpSecurityImpl's nested HttpPermission builder when a policy-defining method (permit, deny, roles, permissions, policy, authenticated) is called after a policy has already been assigned to this permission. Each HttpPermission can carry exactly one authorization policy, so a second call makes the configuration ambiguous and the builder fails fast with IllegalArgumentException.
Source
Thrown at extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/security/HttpSecurityImpl.java:364
@Override
public HttpSecurity policy(Predicate<SecurityIdentity> predicate) {
return policy((identity, request) -> !identity.isAnonymous() && predicate.test(identity));
}
@Override
public HttpSecurity policy(BiPredicate<SecurityIdentity, RoutingContext> predicate) {
return policy(new SimpleHttpSecurityPolicy(predicate));
}
private HttpSecurity authenticated() {
validatePolicyNotSetYet();
this.policy = new Policy(AuthenticatedHttpSecurityPolicy.NAME, null);
return HttpSecurityImpl.this;
}
private void validatePolicyNotSetYet() {
if (policy != null) {
throw new IllegalArgumentException("Policy has already been set");
}
}
}
private final class HttpPermissionImpl implements HttpPermission, HttpPermissionCarrier {
private final String[] paths;
private boolean shared;
private boolean applyToJaxRs;
private String[] methods;
private HttpSecurityConfiguration.AuthenticationMechanisms authMechanism;
private AuthorizationPolicy authorizationPolicy;
private HttpPermissionImpl(String[] paths) {
this.paths = Arrays.copyOf(paths, paths.length);
this.authMechanism = null;
this.authorizationPolicy = null;
this.shared = false;View on GitHub (pinned to e1c734241f)
Solutions
- Remove the duplicate policy call so each HttpPermission has exactly one of permit/deny/roles/permissions/policy/authenticated.
- Create a fresh HttpPermission via HttpSecurity.paths(...) for each distinct rule instead of reusing one instance.
- If combining behavior is intended, use the policy(...) API once with a composed HttpSecurityPolicy.
Example fix
// before
var perm = httpSecurity.paths("/api/*");
perm.permit();
perm.deny(); // IllegalArgumentException
// after
httpSecurity.paths("/api/*").permit();
httpSecurity.paths("/api/admin/*").deny(); Defensive patterns
Strategy: validation
Validate before calling
if (perm.isPolicySet()) throw new IllegalStateException("policy already assigned to this permission"); Try / catch
try { perm.permit(); } catch (IllegalArgumentException e) { if (!e.getMessage().contains("Policy has already been set")) throw e; } Prevention
- One terminal policy call per paths(...) chain.
- Never store and reuse HttpPermission instances across rules.
- Build each rule as a single fluent expression from httpSecurity.paths(...).
When it happens
Trigger: Calling two policy methods on the same HttpPermission instance, e.g. chaining .permit().deny(), .roles("admin").permissions("read"), or .authenticated().permit() on the same object returned from httpSecurity.paths("/x").
Common situations: Reusing a captured HttpPermission variable to build several different rules; copy-pasted fluent chains where an extra policy call was left in; refactors that split one chain into two without creating a new permission via paths().
Related errors
- Roles must not be empty
- Authentication has already been set
- Authorization has already been set
- Name cannot start with '/':${name}
- Predicate already set
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/7b14fb8d8608a33a.
Report an issue: GitHub.