cloudflare/cloudflared · error
metadata JWT type 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.
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
- aud array contains non-string elements
- aud field is not a string or an array of strings
- auth_domain does not end with
- failed to decode metadata JWT claims
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/d25c00eaccab4be9.
Report an issue: GitHub.
Appendix: 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)