quarkusio/quarkus · error · SpiffeConnectionException
JWT-SVID 'aud' claim contains unexpected extra audiences; re
Error message
JWT-SVID 'aud' claim contains unexpected extra audiences; requested: ${requestedAudiences}, received: ${audience} What it means
If the token's aud set strictly contains all requested audiences but also has extra entries, the client treats the token as over-scoped and rejects it (audience.size() != requestedAudiences.size()). This enforces exact audience matching between request and issued JWT-SVID.
Source
Thrown at extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeClientImpl.java:262
audience.add(audienceAsString);
} else {
throw new SpiffeConnectionException(
"JWT-SVID 'aud' array element at index " + i + " is not a string:" + audienceAsArray.getValue(i));
}
}
} else if (aud instanceof String audienceAsString) {
audience = Set.of(audienceAsString);
} else {
throw new SpiffeConnectionException(
"JWT-SVID 'aud' claim is not a string or array of strings");
}
if (!audience.containsAll(requestedAudiences)) {
throw new SpiffeConnectionException(
"JWT-SVID 'aud' claim does not contain the requested audiences; requested: "
+ requestedAudiences + ", received: " + audience);
}
if (audience.size() != requestedAudiences.size()) {
throw new SpiffeConnectionException(
"JWT-SVID 'aud' claim contains unexpected extra audiences; requested: "
+ requestedAudiences + ", received: " + audience);
}
Long exp = payload.getLong("exp");
if (exp == null) {
throw new SpiffeConnectionException("JWT-SVID from SPIRE agent is missing the required 'exp' claim");
}
Instant expiry = Instant.ofEpochSecond(exp);
if (expiry.isBefore(Instant.now())) {
throw new SpiffeConnectionException("JWT-SVID from SPIRE agent is already expired");
}
record WorkloadJsonWebTokenImpl(String token, String subject, Set<String> audience,
Instant expiry) implements WorkloadJsonWebToken {
}
return new WorkloadJsonWebTokenImpl(token, sub, Set.copyOf(audience), expiry);
}View on GitHub (pinned to e1c734241f)
Solutions
- Narrow the SPIRE registration entry so issued tokens contain exactly the required audiences
- Request the full audience set that the entry grants, or update config to match the token contents
- Create separate SPIRE entries per service pairing to keep scopes minimal
Example fix
// before (entry grants a+b, request asks only a)
spiffeClient.getWorkloadJsonWebToken(Set.of("a"));
// after
spiffeClient.getWorkloadJsonWebToken(Set.of("a", "b")); // or narrow the SPIRE entry Defensive patterns
Strategy: validation
Validate before calling
// request exactly the audience set the SPIRE entry grants
Set<String> entryGranted = Set.of("https://api.example.com");
// call with the full set, or narrow the entry with spire-server entry update Try / catch
try {
return spiffeClient.getWorkloadJsonWebToken(requested).await().indefinitely();
} catch (SpiffeConnectionException e) {
if (e.getMessage().contains("unexpected extra audiences")) {
// narrow the SPIRE entry or request all granted audiences
}
throw e;
} Prevention
- Prefer one SPIRE entry per service pairing with minimal audiences
- Review entries after adding new audiences to shared workloads
- Document the exact audience contract between services
When it happens
Trigger: SPIRE entry issues tokens with multiple audiences while the client requested only a subset, e.g. requesting {"a"} but receiving aud ["a","b"].
Common situations: Broad SPIRE registration entries granting many audiences; shared workload entries reused by several services; tightening audiences in app config without narrowing the SPIRE entry (or vice versa).
Related errors
- JWT-SVID 'aud' claim does not contain the requested audience
- JWT-SVID 'aud' array element at index ${i} is not a string:$
- JWT-SVID 'aud' claim is not a string or array of strings
- 'credentials.jwt.source' is set to 'spiffe-jwt', but no audi
- JWT-SVID from SPIRE agent has no token
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/beae742553a98180.
Report an issue: GitHub.