quarkusio/quarkus · error · SpiffeConnectionException
JWT-SVID proto SPIFFE ID does not match the 'sub' claim; pro
Error message
JWT-SVID proto SPIFFE ID does not match the 'sub' claim; proto: ${spiffeId}, sub: ${sub} What it means
The 'sub' claim extracted from the decoded JWT payload is validated as a SPIFFE ID and must match the spiffe_id field of the JWTSVID proto. A mismatch means the token contents do not agree with the agent-reported identity, so the token is untrustworthy and SpiffeConnectionException is thrown.
Source
Thrown at extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeClientImpl.java:231
}));
}
private static WorkloadJsonWebToken toWorkloadJsonWebToken(JWTSVID svid,
Set<String> requestedAudiences) throws SpiffeConnectionException {
String token = svid.getSvid();
if (token.isBlank()) {
throw new SpiffeConnectionException("JWT-SVID from SPIRE agent has no token");
}
String[] parts = token.split("\\.");
if (parts.length != 3) {
throw new SpiffeConnectionException("JWT-SVID from SPIRE agent is not a valid JWS Compact Serialization");
}
JsonObject payload = new JsonObject(new String(Base64.getUrlDecoder().decode(parts[1])));
String sub = payload.getString("sub");
SpiffeValidator.validateSpiffeId(sub);
if (!sub.equals(svid.getSpiffeId())) {
throw new SpiffeConnectionException(
"JWT-SVID proto SPIFFE ID does not match the 'sub' claim; proto: " + svid.getSpiffeId() + ", sub: " + sub);
}
Object aud = payload.getValue("aud");
if (aud == null) {
throw new SpiffeConnectionException("JWT-SVID from SPIRE agent is missing the required 'aud' claim");
}
final Set<String> audience;
if (aud instanceof JsonArray audienceAsArray) {
audience = new HashSet<>(audienceAsArray.size());
for (int i = 0; i < audienceAsArray.size(); i++) {
if (audienceAsArray.getValue(i) instanceof String audienceAsString) {
audience.add(audienceAsString);
} else {
throw new SpiffeConnectionException(
"JWT-SVID 'aud' array element at index " + i + " is not a string:" + audienceAsArray.getValue(i));
}
}View on GitHub (pinned to e1c734241f)
Solutions
- Restart the SPIRE agent and re-fetch the JWT-SVID to clear stale state
- Check spire-server entries for duplicate or conflicting registrations for this workload and remove duplicates
- Upgrade SPIRE agent/server to a version fixing identity/proto mismatches
Defensive patterns
Strategy: try-catch
Try / catch
try {
return spiffeClient.getWorkloadJsonWebToken(audiences).await().indefinitely();
} catch (SpiffeConnectionException e) {
if (e.getMessage().startsWith("JWT-SVID proto SPIFFE ID does not match")) {
// agent stale state: restart agent / re-register workload
}
throw e;
} Prevention
- Restart SPIRE agents after registration changes to clear stale SVID state
- Avoid duplicate registration entries for the same workload
- Treat identity mismatches as security events and log them
When it happens
Trigger: JWT payload sub differs from JWTSVID.getSpiffeId() — e.g. the agent returned metadata for a different workload identity than the token it issued, or the token was swapped/cached incorrectly.
Common situations: SPIRE agent bugs or caching stale SVIDs after registration changes; workload has multiple registration entries and identity resolution changed; tampered or replayed token in a test harness.
Related errors
- 'credentials.jwt.source' is set to 'spiffe-jwt', but no audi
- JWT-SVID from SPIRE agent has no token
- JWT-SVID from SPIRE agent is not a valid JWS Compact Seriali
- JWT-SVID from SPIRE agent is missing the required 'aud' clai
- JWT-SVID 'aud' array element at index ${i} is not a string:$
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/fe62c11a24ee2e20.
Report an issue: GitHub.