apache/pulsar · error · AuthenticationException

Audiences in token is not in expected format: ${object}

Error message

Audiences in token is not in expected format: ${object}

What it means

This branch is reached when the configured audience claim in the JWT is neither a Collection nor a String. The provider only knows how to validate audience values of those two shapes, so any other JSON type (number, boolean, nested object/array of non-strings) is rejected with this AuthenticationException. The comment 'should not reach here' marks it as a defensive case for malformed tokens.

Source

Thrown at pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java:260

                if (object instanceof Collection) {
                    Collection<String> audiences = (Collection<String>) object;
                    // audience not contains this broker, throw exception.
                    if (audiences.stream().noneMatch(audienceInToken -> audienceInToken.equals(audience))) {
                        incrementFailureMetric(ErrorCode.INVALID_AUDIENCES);
                        throw new AuthenticationException("Audiences in token: ["
                                + String.join(", ", audiences) + "] not contains this broker: " + audience);
                    }
                } else if (object instanceof String) {
                    if (!object.equals(audience)) {
                        incrementFailureMetric(ErrorCode.INVALID_AUDIENCES);
                        throw new AuthenticationException(
                                "Audiences in token: [" + object + "] not contains this broker: " + audience);
                    }
                } else {
                    // should not reach here.
                    incrementFailureMetric(ErrorCode.INVALID_AUDIENCES);
                    throw new AuthenticationException("Audiences in token is not in expected format: " + object);
                }
            }

            var expiration = jwt.getBody().getExpiration();
            var tokenRemainingDurationMs = expiration != null ? expiration.getTime() - new Date().getTime() : null;
            authenticationMetricsToken.recordTokenDuration(tokenRemainingDurationMs);
            return jwt;
        } catch (JwtException e) {
            if (e instanceof ExpiredJwtException) {
                authenticationMetricsToken.recordTokenExpired();
            }
            incrementFailureMetric(ErrorCode.INVALID_TOKEN);
            throw new AuthenticationException("Failed to authentication token: " + e.getMessage());
        }
    }

    private String getPrincipal(Jws<Claims> jwt) {
        try {

View on GitHub (pinned to 820761864e)

Solutions

  1. Re-issue the token so the audience claim is either a string or an array of strings.
  2. Fix the token issuer's claim generation so it emits the standard aud format (string or list of strings).
  3. Verify the configured tokenAudienceClaim points at the intended claim; if the claim name collides with a non-audience field, correct it.
  4. If audience checking is unnecessary, disable it by removing the audience claim configuration.

Example fix

// before (issuer): payload.put("aud", 12345)
// after (issuer): payload.put("aud", "urn:my-broker")
Defensive patterns

Strategy: type-guard

Validate before calling

// Before using a token, assert the audience claim type:
Object aud = claims.get("aud");
boolean formatOk = aud instanceof String || aud instanceof java.util.Collection<?>;

Type guard

static boolean isSupportedAudienceFormat(Object claimValue) {
    return claimValue instanceof String
        || (claimValue instanceof Collection<?> c && c.stream().allMatch(String.class::isInstance));
}

Try / catch

try {
    String role = authProvider.authenticate(authData);
} catch (AuthenticationException e) {
    if (e.getMessage().startsWith("Audiences in token is not in expected format")) {
        throw new IllegalStateException("Token issuer emits audience claim with unsupported JSON type; fix the issuer", e);
    }
}

Prevention

When it happens

Trigger: authenticateToken parses a token where jwt.getBody().get(audienceClaim) returns an object that is not a Collection<String> or String — e.g. the aud-style claim was issued as a number or a map — while audience validation is enabled.

Common situations: A custom token issuer serializes the audience claim in an unexpected type (e.g. numeric realm ID); a misconfigured claim name points at a claim that holds a non-string value; a JSON library upgrade changes deserialization of the claim.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/545a85f40a9b255c. Report an issue: GitHub.