apache/pulsar · error · JwtException
Found null Audience in token, for claimed field: ${audienceC
Error message
Found null Audience in token, for claimed field: ${audienceClaim} What it means
authenticateToken() enforces audience validation when audienceClaim is configured: the JWT body must contain that claim. This check throws io.jsonwebtoken.JwtException (which authenticateToken wraps/rethrows) when the claim key exists in the config but the token's payload has a null/absent value for it — the token was issued without the audience claim the broker requires.
Source
Thrown at pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationProviderToken.java:240
}
private static String validateToken(final String token) throws AuthenticationException {
if (StringUtils.isNotBlank(token)) {
return token;
} else {
throw new AuthenticationException("Blank token found");
}
}
@SuppressWarnings("unchecked")
private Jws<Claims> authenticateToken(final String token) throws AuthenticationException {
try {
Jws<Claims> jwt = parser.parseClaimsJws(token);
if (audienceClaim != null) {
Object object = jwt.getBody().get(audienceClaim);
if (object == null) {
throw new JwtException("Found null Audience in token, for claimed field: " + audienceClaim);
}
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.View on GitHub (pinned to 820761864e)
Solutions
- Re-issue the token including the audience claim with the broker's audience value
- Align the token issuer's claim name with the broker's tokenAudienceClaim setting
- If audience validation is not needed, remove tokenAudienceClaim from broker config
Example fix
// before: token without aud claim
String token = Jwts.builder().setSubject("admin").signWith(key).compact();
// after
String token = Jwts.builder().setSubject("admin")
.claim("aud", "pulsar") // or setAudience("pulsar")
.signWith(key).compact(); Defensive patterns
Strategy: validation
Validate before calling
Claims claims = Jwts.parser().setSigningKey(key).build()
.parseClaimsJws(token).getBody();
if (claims.get("aud") == null) {
throw new JwtException("Token missing required audience claim");
} Type guard
boolean hasAudienceClaim(Jws<Claims> jwt, String claim) {
return jwt != null && jwt.getBody().get(claim) != null;
} Try / catch
try {
role = provider.authenticate(authData);
} catch (Exception e) {
log.warn("Token rejected: missing audience claim; re-issue token with aud", e);
return 401;
} Prevention
- Ensure the token issuer always adds the audience claim matching tokenAudienceClaim
- Re-issue tokens whenever the claim name config changes
- Add a token smoke test to deployment pipelines
When it happens
Trigger: A token parsed by parser.parseClaimsJws(token) whose Claims map returns null for the configured audienceClaim key, while the broker has tokenAudienceClaim set.
Common situations: Tokens issued by an older token issuer or a different tool that doesn't add the 'aud' (or custom) claim; changing the audienceClaim config name (e.g. from 'aud' to a custom name) without re-issuing tokens; mixing tokens from multiple issuers with different claim layouts.
Related errors
- INVALID_JWT_CLAIM
- Token Audience Claim [${audienceClaim}] configured, but Audi
- No token credentials passed
- Blank token found
- Audiences in token: [${audiences}] not contains this broker:
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/7d13be4dedd9082c.
Report an issue: GitHub.