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

  1. Synchronize clocks (NTP/chrony) across the workload host and SPIRE agent and retry token fetch
  2. Re-fetch the JWT-SVID instead of reusing a previously obtained token; refresh on each expiry window
  3. 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

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


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/c20d20609e1e6da8. Report an issue: GitHub.