quarkusio/quarkus · error · SpiffeConnectionException

JWT-SVID 'aud' array element at index

Error message

JWT-SVID 'aud' array element at index ${i} is not a string:${value}

What it means

When the 'aud' claim is a JSON array, every element must be a string per JWT spec. The client iterates the array and throws SpiffeConnectionException at the first non-string element, since audiences must be comparable to the requested set of strings.

Solutions

  1. Correct the token source so all aud array elements are strings
  2. Replace stubbed JWTs with real ones fetched via spire-agent api fetch jwt
  3. Check for middleware/proxies that decode and re-encode JWT payloads with altered types

Example fix

// before
{"aud":["https://api.example.com", 42]}
// after
{"aud":["https://api.example.com", "https://other.example.com"]}
Defensive patterns

Strategy: type-guard

Type guard

static boolean audClaimIsValid(Object aud) {
    if (aud instanceof String) return true;
    if (aud instanceof JsonArray arr) {
        for (int i = 0; i < arr.size(); i++) {
            if (!(arr.getValue(i) instanceof String)) return false;
        }
        return true;
    }
    return false;
}

Try / catch

try {
    return spiffeClient.getWorkloadJsonWebToken(audiences).await().indefinitely();
} catch (SpiffeConnectionException e) {
    if (e.getMessage().contains("'aud' array element")) {
        // fix token producer / replace stubs
    }
    throw e;
}

Prevention

When it happens

Trigger: Decoded payload contains aud as an array with numeric/boolean/object/null elements — typically from a hand-built or corrupted token rather than a real SPIRE-issued SVID.

Common situations: Test fixtures with wrongly typed audience values; JSON encoding mistakes when replaying captured tokens; intermediary services rewriting the token payload.

Related errors


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

Appendix: source

Thrown at extensions/spiffe-client/runtime/src/main/java/io/quarkus/spiffe/client/runtime/internal/SpiffeClientImpl.java:246

        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));
                }
            }
        } 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);

View on GitHub (pinned to e1c734241f)