cloudflare/cloudflared · error
metadata JWT type %q is not match
Error message
metadata JWT type %q is not match
What it means
GetAppInfo fetches a Cloudflare Access metadata JWT from the team domain and validates its claims before returning app info. After verifying the hostname, it checks that the JWT's `type` claim equals the expected value "match" (metadataMatchType). If the server returned a metadata JWT with any other type value, GetAppInfo rejects it with this error because the token does not certify that the requested host matches the Access application.
Source
Thrown at token/token.go:453
// Parse auth_domain into the canonical hostname used for JWKS lookup.
authDomain, err := parseAuthDomain(unverified.AuthDomain)
if err != nil {
return nil, fmt.Errorf("metadata JWT auth_domain validation failed: %w", err)
}
// Verify the JWT signature against the JWKS (with disk cache + retry).
claims, err := verifyMetadataWithRetry(rawJWT, authDomain)
if err != nil {
return nil, fmt.Errorf("metadata JWT verification failed: %w", err)
}
// Verify the hostname in the JWT matches the URL we actually requested.
if !strings.EqualFold(claims.Hostname, reqURL.Hostname()) {
return nil, fmt.Errorf("metadata JWT hostname %q does not match request host %q", claims.Hostname, reqURL.Hostname())
}
if claims.Type != metadataMatchType {
return nil, fmt.Errorf("metadata JWT type %q is not match", claims.Type)
}
if claims.AUD == "" {
return nil, errors.New("metadata JWT aud is empty")
}
if err := validateMetadataIssuedAt(claims.IAT, time.Now()); err != nil {
return nil, err
}
appHostname := claims.AppHostname
if appHostname == "" {
// For retro-compatibility with CF access older releases, this will cause wildcard apps to store one local token
// per requested hostname, which is less optimized but also works.
appHostname = claims.Hostname
}
return &AppInfo{
AuthDomain: authDomain.Hostname(),
AppAUD: claims.AUD,View on GitHub (pinned to 2253eeeb25)
Solutions
- Verify the URL passed to GetAppInfo is the exact hostname of a Cloudflare Access application (not the team domain or a generic login URL).
- Check the Access application configuration in the Cloudflare Zero Trust dashboard covers that hostname with an application of type self-hosted.
- Clear any intermediate proxy/cache and retry so a fresh cf-access-metadata JWT is fetched.
- Capture the raw JWT from the cf-access-metadata header (e.g. jwt.io) and inspect the `type` claim to confirm what the edge is returning.
- Update cloudflared to the latest version in case the metadata type contract changed server-side.
Example fix
// before
appInfo, err := token.GetAppInfo("https://myteam.cloudflareaccess.com", "https://internal.example.com")
// after — ensure the app URL is the protected origin, not the login/team domain
appInfo, err := token.GetAppInfo("https://myteam.cloudflareaccess.com", "https://app.internal.example.com") Defensive patterns
Strategy: validation
Validate before calling
// fetch the raw metadata JWT and inspect the type claim before relying on GetAppInfo
resp, err := http.Get(appURL)
if err != nil { return err }
raw := resp.Header.Get("cf-access-metadata")
if raw == "" { return errors.New("no metadata JWT; URL likely not behind Access") }
parts := strings.Split(raw, ".")
if len(parts) != 3 { return errors.New("malformed metadata JWT") }
payload, _ := base64.RawURLEncoding.DecodeString(parts[1])
var claims struct{ Type string `json:"type"` }
if err := json.Unmarshal(payload, &claims); err != nil { return err }
if claims.Type != "match" { return fmt.Errorf("unexpected metadata JWT type %q", claims.Type) } Try / catch
appInfo, err := token.GetAppInfo(authDomain, appURL)
if err != nil {
if strings.Contains(err.Error(), "is not match") {
// URL is not certified by Access metadata; surface a config hint
return fmt.Errorf("%w — verify %s is a Cloudflare Access application hostname", err, appURL)
}
return err
} Prevention
- Always pass the exact protected hostname, not the team domain or login URL.
- Validate the app is configured as a self-hosted Access application before integrating.
- Log the raw cf-access-metadata JWT when debugging metadata-type mismatches.
- Pin cloudflared versions and test after Cloudflare Zero Trust policy changes.
When it happens
Trigger: Calling GetAppInfo (directly or via login, curl, generateToken, sshGen, createWebsocketStream) against a Cloudflare Access-protected domain when the cf-access-metadata response header contains a JWT whose `type` claim is not "match" — e.g. the URL does not correspond to a specific Access application, or the edge returned a differently-typed token.
Common situations: Requesting a domain protected by a wildcard/self-hosted Access app where the metadata endpoint answers with a non-matching token type; a Cloudflare-side change or misconfigured Access application; hitting a login page URL instead of the app's own hostname; proxy/CDN caching an unrelated metadata JWT.
Related errors
- metadata JWT auth_domain validation failed: %w
- invalid token
- aud array contains non-string elements
- aud field is not a string or an array of strings
- metadata JWT aud is empty
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/d25c00eaccab4be9.
Report an issue: GitHub.