quarkusio/quarkus · error · SpiffeConnectionException
JWT-SVID from SPIRE agent is missing the required 'exp' clai
Error message
JWT-SVID from SPIRE agent is missing the required 'exp' claim
What it means
A JWT-SVID must include an 'exp' (expiry) claim so the client can determine validity; a missing exp makes lifetime handling impossible and is rejected. Additionally, if the token is already expired the client throws 'JWT-SVID from SPIRE agent is already expired' from the same block.
Source
Thrown at extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeClientImpl.java:269
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);
}
private static WorkloadCertificateDocument toWorkloadCertificate(X509SVIDResponse response)
throws SpiffeConnectionException {
List<X509SVID> svids = response.getSvidsList();
if (svids.isEmpty()) {
throw new SpiffeConnectionException("X.509-SVID response from SPIRE agent contains no SVIDs");
}View on GitHub (pinned to e1c734241f)
Solutions
- Synchronize clocks (NTP/chrony) across the workload host and SPIRE agent and retry token fetch
- Re-fetch the JWT-SVID instead of reusing a previously obtained token; refresh on each expiry window
- Update test fixtures to include a future exp value
Example fix
// before
{"sub":"spiffe://example.org/workload","aud":["api"]}
// after
{"sub":"spiffe://example.org/workload","aud":["api"],"exp":1757000000} Defensive patterns
Strategy: retry
Validate before calling
// check token TTL before reuse long maxAgeSeconds = 300; // typical SPIRE default boolean likelyFresh = fetchedAtEpoch + maxAgeSeconds > Instant.now().getEpochSecond();
Try / catch
try {
return spiffeClient.getWorkloadJsonWebToken(audiences).await().indefinitely();
} catch (SpiffeConnectionException e) {
if (e.getMessage().contains("already expired") || e.getMessage().contains("'exp'")) {
return spiffeClient.getWorkloadJsonWebToken(audiences).await().indefinitely(); // re-fetch
}
throw e;
} Prevention
- Run NTP on hosts and containers
- Re-fetch tokens instead of caching past exp
- Refresh at 80% of TTL
When it happens
Trigger: Payload lacks the exp claim, or exp is in the past (Instant.ofEpochSecond(exp).isBefore(Instant.now())) — e.g. reusing cached tokens, clock skew between host and SPIRE agent, or stubs without exp.
Common situations: NIST/clock skew in containers or VMs causing freshly issued tokens to appear expired; caching a WorkloadJsonWebToken past its TTL; synthetic test tokens missing standard claims.
Related errors
- JWT-SVID from SPIRE agent is missing the required 'aud' clai
- '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 proto SPIFFE ID does not match the 'sub' claim; pro
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/c20d20609e1e6da8.
Report an issue: GitHub.