cloudflare/cloudflared · error
failed to decode metadata JWT claims
Error message
failed to decode metadata JWT claims
What it means
After parsing the JWS, decodeMetadataUnverified extracts the payload with UnsafePayloadWithoutVerification and json.Unmarshal's it into metadataClaims. This error means the payload decodes but is not valid JSON matching the claims structure (e.g. `aud` is a non-string). It is surfaced by GetAppInfo.
Source
Thrown at token/jwks.go:54
AuthDomain string `json:"auth_domain"`
AUD string `json:"aud"`
// This is the hostname as defined in the Access application, including wildcards.
AppHostname string `json:"app_hostname"`
IAT int64 `json:"iat"`
}
// decodeMetadataUnverified decodes the JWT payload without verifying the
// signature.
func decodeMetadataUnverified(rawJWT string) (*metadataClaims, error) {
jws, err := jose.ParseSigned(rawJWT, signatureAlgs)
if err != nil {
return nil, errors.Wrap(err, "failed to parse metadata JWT")
}
payload := jws.UnsafePayloadWithoutVerification()
var claims metadataClaims
if err := json.Unmarshal(payload, &claims); err != nil {
return nil, errors.Wrap(err, "failed to decode metadata JWT claims")
}
return &claims, nil
}
// verifyMetadataJWT verifies the metadata JWT signature against the provided
// JWKS and returns the decoded claims.
func verifyMetadataJWT(rawJWT string, keySet *jose.JSONWebKeySet) (*metadataClaims, error) {
jws, err := jose.ParseSigned(rawJWT, signatureAlgs)
if err != nil {
return nil, errors.Wrap(err, "failed to parse metadata JWT")
}
payload, err := jws.Verify(keySet)
if err != nil {
return nil, errors.Wrap(err, "failed to verify metadata JWT signature")
}
var claims metadataClaimsView on GitHub (pinned to 2253eeeb25)
Solutions
- Ensure the token is the Access metadata JWT (obtained via CF-Access-Metadata-Request: true), not an ID or session token.
- Base64url-decode the payload segment and inspect it with `jq` to confirm it contains hostname/auth_domain/aud claims.
- Do not modify or re-sign the token before passing it in.
- Check that no proxy rewrites the token value (trailing newlines, double encoding).
Example fix
// sanity-check before calling
parts := strings.Split(token, ".")
if len(parts) != 2 && len(parts) != 3 {
return errors.New("unexpected JWT shape")
}
payload, _ := base64.RawURLEncoding.DecodeString(parts[1])
var probe map[string]any
if err := json.Unmarshal(payload, &probe); err != nil {
return fmt.Errorf("not an Access metadata JWT payload: %w", err)
}
claims, err := GetAppInfo(token) Defensive patterns
Strategy: validation
Validate before calling
func payloadIsJSONClaims(token string) error {
parts := strings.Split(strings.TrimSpace(token), ".")
if len(parts) < 2 {
return errors.New("not a JWT")
}
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return err
}
var probe map[string]any
return json.Unmarshal(payload, &probe)
} Try / catch
claims, err := GetAppInfo(rawJWT)
if err != nil && strings.Contains(err.Error(), "failed to decode metadata JWT claims") {
return fmt.Errorf("payload is not an Access metadata claims set — is this the right token type? %w", err)
} Prevention
- Only feed tokens obtained via the metadata endpoint (CF-Access-Metadata-Request: true).
- Inspect the payload once with jq during integration to confirm expected claims.
- Do not re-serialize or transform tokens in middleware.
When it happens
Trigger: Calling GetAppInfo with a JWT whose payload segment contains valid base64 but not JSON-decodable metadataClaims — empty payload, binary payload, JSON with incompatible types (aud as array), or a token from a non-Access issuer.
Common situations: Passing an OIDC ID token or other JWT instead of the Cloudflare Access metadata JWT; a payload that was re-encoded/modified client-side; automated tools that re-serialize the token and mangle the payload.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to decode verified metadata JWT claims
- invalid token
- metadata JWT aud is empty
- metadata JWT iat is missing or invalid
- failed to parse auth_domain %q: %w
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/ed45f8ee30ef8ea4.
Report an issue: GitHub.