{"record":{"id":"c1c87253152682d1","repo":"cloudflare/cloudflared","slug":"token-is-nil-s","errorCode":null,"errorMessage":"token is nil: %s","messagePattern":"token is nil: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"validation/validation.go","lineNumber":204,"sourceCode":"\n\t// An issuerURL from Cloudflare Access will always use HTTPS.\n\tissuerURL = strings.Replace(issuerURL, \"http:\", \"https:\", 1)\n\n\tkeySet := oidc.NewRemoteKeySet(ctx, domainURL+accessCertPath)\n\treturn &Access{oidc.NewVerifier(issuerURL, keySet, &oidc.Config{ClientID: applicationAUD})}, nil\n}\n\nfunc (a *Access) Validate(ctx context.Context, jwt string) error {\n\ttoken, err := a.verifier.Verify(ctx, jwt)\n\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"token is invalid: %s\", jwt)\n\t}\n\n\t// Perform extra sanity checks, just to be safe.\n\n\tif token == nil {\n\t\treturn fmt.Errorf(\"token is nil: %s\", jwt)\n\t}\n\n\tif !strings.HasSuffix(token.Issuer, accessDomain) {\n\t\treturn fmt.Errorf(\"token has non-cloudflare issuer of %s: %s\", token.Issuer, jwt)\n\t}\n\n\treturn nil\n}\n\nfunc (a *Access) ValidateRequest(ctx context.Context, r *http.Request) error {\n\treturn a.Validate(ctx, r.Header.Get(accessJwtHeader))\n}\n","sourceCodeStart":186,"sourceCodeEnd":217,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/validation/validation.go#L186-L217","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// defensive caller-side guard\nif jwt == \"\" {\n    return errors.New(\"missing Cf-Access-Jwt-Assertion header\")\n}\nif err := validator.Validate(ctx, jwt); err != nil {\n    return fmt.Errorf(\"access denied: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"func hasJWT(r *http.Request) bool {\n    return r.Header.Get(\"Cf-Access-Jwt-Assertion\") != \"\"\n}","typeGuard":"func nonEmptyJWT(jwt string) bool { return jwt != \"\" }","tryCatchPattern":"if err := validator.Validate(ctx, jwt); err != nil {\n    log.Warn().Err(err).Msg(\"access token sanity check failed\")\n    http.Error(w, \"Unauthorized\", http.StatusUnauthorized) // fail closed\n}","preventionTips":["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"],"tags":["jwt","oidc","access","invariant"],"backgroundTag":"jwt-token-validation-failed","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}