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

  1. Restart the SPIRE agent and re-fetch the JWT-SVID to clear stale state
  2. Check spire-server entries for duplicate or conflicting registrations for this workload and remove duplicates
  3. 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

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


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