keycloak/keycloak · error · RuntimeException

Could not parse roles from config: [{}]

Error message

Could not parse roles from config: [{}]

What it means

Thrown by RolePolicyProviderFactory.getRoles() when Jackson cannot parse the raw 'roles' JSON string from the policy config into a RoleDefinition[] array. getRoles() is called from toRepresentation(), so this fires when reading/viewing a role policy whose stored config JSON is malformed. The raw JSON string is included in the message for diagnosis.

Source

Thrown at authz/policy/common/src/main/java/org/keycloak/authorization/policy/provider/role/RolePolicyProviderFactory.java:208

    @Override
    public void close() {

    }

    @Override
    public String getId() {
        return ID;
    }

    private Set<RoleDefinition> getRoles(String rawRoles, RealmModel realm) {
        if (rawRoles != null) {
            try {
                return Arrays.stream(JsonSerialization.readValue(rawRoles, RoleDefinition[].class))
                        .filter(definition -> getRole(definition, realm) != null)
                        .sorted()
                        .collect(Collectors.toCollection(LinkedHashSet::new));
            } catch (IOException e) {
                throw new RuntimeException("Could not parse roles from config: [" + rawRoles + "]", e);
            }
        }

        return Collections.emptySet();
    }

    public static final Pattern UUID_PATTERN = Pattern.compile("[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}");

    private RoleModel getRole(RolePolicyRepresentation.RoleDefinition definition, RealmModel realm) {
        String roleName = definition.getId();
        String clientId = null;
        int clientIdSeparator = roleName.indexOf("/");

        if (clientIdSeparator != -1) {
            clientId = roleName.substring(0, clientIdSeparator);
            roleName = roleName.substring(clientIdSeparator + 1);
        }

View on GitHub (pinned to 66c7e15a37)

Solutions

  1. Inspect config['roles'] in the policy (via DB or admin API) and validate it with a JSON parser.
  2. Fix the JSON to match the expected RoleDefinition schema [{"id":"role-uuid","required":true}].
  3. If unrecoverable, delete and recreate the role policy through the admin console.
  4. If from a realm import, fix the source export file's role policy config.

Example fix

// before: config["roles"] = "[{id: 'broken'}]"
// after:  config["roles"] = "[{\"id\":\"role-uuid\",\"required\":true}]"
Defensive patterns

Strategy: try-catch

Validate before calling

// Before reading a role policy, validate the stored roles JSON
String rolesJson = policy.getConfig().get("roles");
if (rolesJson != null) {
    try {
        RoleDefinition[] defs = JsonSerialization.readValue(rolesJson, RoleDefinition[].class);
        // valid — safe to call toRepresentation()
    } catch (IOException e) {
        logger.warn("Role policy config is corrupt: " + e.getMessage());
    }
}

Try / catch

try {
    RolePolicyRepresentation rep = factory.toRepresentation(policy, authorization);
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Could not parse roles from config")) {
        logger.error("Corrupt role policy config for: " + policy.getName(), e);
        // recreate the policy from scratch
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: GET .../policy/role/{policyId} for a role policy whose config['roles'] is corrupt JSON. Also triggered internally during evaluation when the role policy representation is resolved.

Common situations: Database-level corruption of the policy config. A failed migration or import leaving truncated/malformed JSON. Hand-editing the policy config directly in the DB. Version upgrade where the RoleDefinition schema changed without migration.

Related errors


AI-assisted analysis of keycloak/keycloak@66c7e15a37 (2026-08-14). Data as JSON: /api/errors/eaef963a42ec1790. Report an issue: GitHub.