cloudflare/cloudflared · warning
token is nil: %s
Error message
token is nil: %s
What it means
Access.Validate verifies the Cloudflare Access JWT with the OIDC verifier, then runs sanity checks. This error means Verify returned successfully but produced a nil token — a defensive invariant check that should be nearly unreachable; if it fires, the verifier/key-set setup is suspect. The full JWT is included in the message.
Source
Thrown at validation/validation.go:204
// An issuerURL from Cloudflare Access will always use HTTPS.
issuerURL = strings.Replace(issuerURL, "http:", "https:", 1)
keySet := oidc.NewRemoteKeySet(ctx, domainURL+accessCertPath)
return &Access{oidc.NewVerifier(issuerURL, keySet, &oidc.Config{ClientID: applicationAUD})}, nil
}
func (a *Access) Validate(ctx context.Context, jwt string) error {
token, err := a.verifier.Verify(ctx, jwt)
if err != nil {
return errors.Wrapf(err, "token is invalid: %s", jwt)
}
// Perform extra sanity checks, just to be safe.
if token == nil {
return fmt.Errorf("token is nil: %s", jwt)
}
if !strings.HasSuffix(token.Issuer, accessDomain) {
return fmt.Errorf("token has non-cloudflare issuer of %s: %s", token.Issuer, jwt)
}
return nil
}
func (a *Access) ValidateRequest(ctx context.Context, r *http.Request) error {
return a.Validate(ctx, r.Header.Get(accessJwtHeader))
}
View on GitHub (pinned to 2253eeeb25)
Solutions
- Log the JWT and report/inspect why oidc IDTokenVerifier.Verify returned nil token with nil error
- Re-create the validator with correct NewAccessValidator(domain, issuer, aud) arguments and confirm key set URL (domain + /cdn-cgi/access/certs)
- Pin/upgrade github.com/coreos/go-oidc/v3 to a version where Verify never returns (nil, nil) for a non-empty jwt
- Guard callers with an explicit token!=nil check and fail closed
Example fix
// defensive caller-side guard
if jwt == "" {
return errors.New("missing Cf-Access-Jwt-Assertion header")
}
if err := validator.Validate(ctx, jwt); err != nil {
return fmt.Errorf("access denied: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
func hasJWT(r *http.Request) bool {
return r.Header.Get("Cf-Access-Jwt-Assertion") != ""
} Type guard
func nonEmptyJWT(jwt string) bool { return jwt != "" } Try / catch
if err := validator.Validate(ctx, jwt); err != nil {
log.Warn().Err(err).Msg("access token sanity check failed")
http.Error(w, "Unauthorized", http.StatusUnauthorized) // fail closed
} Prevention
- Reject requests missing the Cf-Access-Jwt-Assertion header before validation
- Keep go-oidc updated to a version whose Verify never returns (nil, nil)
- Fail closed (401) on any Validate error
When it happens
Trigger: a.verifier.Verify(ctx, jwt) returning (nil, nil) — effectively an internal invariant violation in the oidc verifier integration; reached via Access.Validate or Access.ValidateRequest (Cf-Access-Jwt-Assertion header).
Common situations: Practically only seen with a misconfigured NewAccessValidator or an unexpected oidc library version/behavior; treat it as a bug indicator rather than a user input problem.
Related errors
- token has non-cloudflare issuer of %s: %s
- empty application token
- incorrect args
- not a valid url
- not a valid host
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/c1c87253152682d1.
Report an issue: GitHub.