quarkusio/quarkus · error · IllegalStateException

The certificate to roles mapper is already configured with…

Error message

The certificate to roles mapper is already configured with the 'certificateToRolesMapper' method

What it means

MTLS.Builder supports two mutually exclusive ways to map client certificates to SecurityIdentity roles: attribute-based role mapping via rolesMapping/certificateAttribute, or a custom function via certificateToRolesMapper. This IllegalStateException is thrown from assertCertificateToRolesMapperNotSetYet when a builder already had the custom mapper set and you then attempt to configure the attribute/roles-mapping path. Only one strategy may be configured per builder.

Solutions

  1. Pick one strategy: remove the certificateToRolesMapper(...) call if you want attribute-based rolesMapping/certificateAttribute
  2. Or remove the rolesMapping/certificateAttribute call and keep only the custom certificateToRolesMapper function
  3. If both behaviors are needed, implement the attribute-based logic inside the single certificateToRolesMapper function

Example fix

// before
mtls.certificateToRolesMapper(cert -> Set.of("admin"))
    .certificateAttribute("CN"); // IllegalStateException
// after
mtls.certificateToRolesMapper(cert -> {
    String cn = ...; // fold attribute logic into the mapper
    return Set.of("admin");
});
Defensive patterns

Strategy: validation

Validate before calling

if (builderMapperSet && wantAttributeMapping) { throw new IllegalArgumentException("choose either certificateToRolesMapper or rolesMapping, not both"); }

Try / catch

try { mtls.certificateAttribute("CN"); } catch (IllegalStateException e) { log.warn("Mapper already configured; keeping certificateToRolesMapper"); }

Prevention

When it happens

Trigger: Calling MTLS.Builder.certificateAttribute(...) or rolesMapping(...) after certificateToRolesMapper(Function<X509Certificate, Set<String>>) has already been invoked on the same Builder instance.

Common situations: Programmatically building HttpSecurity with mTLS auth where code paths merge — e.g. default configuration sets a custom mapper and then an extension or user code adds rolesMapping; copy-pasted builder chains that configure both mechanisms.

Understand the failure class

Related errors


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

Appendix: source

Thrown at extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/security/MTLS.java:259

            if (roles == null || roles.isEmpty()) {
                throw new IllegalArgumentException("Roles cannot be null or empty");
            }
            assertCertificateToRolesMapperNotSetYet();
            if (certificateAttributeValueToRoles == null) {
                certificateAttributeValueToRoles = new HashMap<>();
            }
            certificateAttributeValueToRoles.computeIfAbsent(certificateAttributeValue, new Function<String, Set<String>>() {
                @Override
                public Set<String> apply(String ignored) {
                    return new HashSet<>();
                }
            }).addAll(roles);
            return this;
        }

        private void assertCertificateToRolesMapperNotSetYet() {
            if (certificateToRolesMapper != null) {
                throw new IllegalStateException(
                        "The certificate to roles mapper is already configured with the 'certificateToRolesMapper' method");
            }
        }

        /**
         * Check the values of different client certificate attributes and map them to the {@link SecurityIdentity} roles.
         *
         * @param certificateToRolesMapper a client certificate to the {@link SecurityIdentity} roles mapper
         * @return Builder
         */
        public Builder certificateToRolesMapper(Function<X509Certificate, Set<String>> certificateToRolesMapper) {
            if (certificateAttributeValueToRoles != null) {
                throw new IllegalStateException(
                        "The certificate to roles mapper is already configured with the 'rolesMapping' method");
            }
            assertCertificateToRolesMapperNotSetYet();
            this.certificateToRolesMapper = certificateToRolesMapper;
            return this;

View on GitHub (pinned to e1c734241f)