quarkusio/quarkus · error · IllegalArgumentException

Target roles for role '%s' must not be empty

Error message

Target roles for role '%s' must not be empty

What it means

The per-entry BiConsumer in rolesMapping(Map) also rejects entries whose target-roles list is null or empty, using a formatted message that names the offending source role. Target roles are the roles granted to identities holding the source role; an entry with no targets is meaningless and would produce a dead policy, so the library fails fast during RolesMapping.of preparation.

Source

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

        return path(paths).methods("DELETE");
    }

    @Override
    public HttpSecurity rolesMapping(Map<String, List<String>> roleToRoles) {
        if (rolesMapping != null) {
            throw new IllegalStateException("Roles mapping is already configured");
        }
        if (roleToRoles == null || roleToRoles.isEmpty()) {
            throw new IllegalArgumentException("Roles must not be empty");
        }
        roleToRoles.forEach(new BiConsumer<String, List<String>>() {
            @Override
            public void accept(String sourceRole, List<String> targetRoles) {
                if (sourceRole.isEmpty()) {
                    throw new IllegalArgumentException("Source role must not be empty");
                }
                if (targetRoles == null || targetRoles.isEmpty()) {
                    throw new IllegalArgumentException("Target roles for role '%s' must not be empty".formatted(sourceRole));
                }
            }
        });

        this.rolesMapping = RolesMapping.of(roleToRoles);
        return this;
    }

    @Override
    public HttpSecurity rolesMapping(String sourceRole, List<String> targetRoles) {
        if (sourceRole == null) {
            throw new IllegalArgumentException("Source role must not be null");
        }
        if (targetRoles == null) {
            throw new IllegalArgumentException("Target roles for role '%s' must not be null".formatted(sourceRole));
        }
        return rolesMapping(Map.of(sourceRole, targetRoles));
    }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Ensure every entry in the map has at least one target role before calling rolesMapping; drop entries with null/empty targets.
  2. If the target list is derived from config, skip the whole entry (or the whole call) when the list resolves to empty.
  3. Fix the data source so the source role has an explicit, non-empty list of target roles.

Example fix

// before
mapping.put("user", filteredRoles); // filteredRoles may be empty
httpSecurity.rolesMapping(mapping);
// after
if (!filteredRoles.isEmpty()) {
    mapping.put("user", filteredRoles);
}
if (!mapping.isEmpty()) {
    httpSecurity.rolesMapping(mapping);
}
Defensive patterns

Strategy: validation

Validate before calling

boolean validTargets = roleToRoles.values().stream()
    .allMatch(v -> v != null && !v.isEmpty());
if (validTargets) {
    httpSecurity.rolesMapping(roleToRoles);
}

Type guard

static boolean hasTargets(Map.Entry<String, List<String>> e) {
    return e.getValue() != null && !e.getValue().isEmpty();
}

Try / catch

try {
    httpSecurity.rolesMapping(mapping);
} catch (IllegalArgumentException e) {
    // message names the offending source role
    log.error("Role mapping rejected: " + e.getMessage());
}

Prevention

When it happens

Trigger: rolesMapping(Map.of("user", List.of())) or rolesMapping("user", null) delegated through Map.of("user", null) (note Map.of itself would NPE on a null value, so null lists typically arrive via HashMap); building lists dynamically where an empty list remained after filtering.

Common situations: Assembling target roles from config where an optional roles list was absent; stripping roles the application does not recognize and ending up with an empty list; JDBC/JSON-driven mappings with an empty targets array.

Related errors


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