{"record":{"id":"f5cf61f47ed2283e","repo":"cloudflare/cloudflared","slug":"failed-to-read-jwks-response-body","errorCode":null,"errorMessage":"failed to read JWKS response body","messagePattern":"failed to read JWKS response body","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"token/jwks.go","lineNumber":116,"sourceCode":"\tclient := &http.Client{\n\t\tCheckRedirect: func(_ *http.Request, _ []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t},\n\t\tTimeout: time.Second * 10,\n\t}\n\tresp, err := client.Get(jwksURL.String()) // nolint: gosec\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"failed to fetch JWKS from %s\", jwksURL.String())\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"JWKS endpoint %s returned status %d\", jwksURL.String(), resp.StatusCode)\n\t}\n\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, maxJWKSResponseSize+1))\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to read JWKS response body\")\n\t}\n\tif len(body) > maxJWKSResponseSize {\n\t\treturn nil, fmt.Errorf(\"JWKS response body exceeds %d bytes\", maxJWKSResponseSize)\n\t}\n\n\tvar keySet jose.JSONWebKeySet\n\tif err := json.Unmarshal(body, &keySet); err != nil {\n\t\treturn nil, errors.Wrap(err, \"failed to parse JWKS\")\n\t}\n\treturn &keySet, nil\n}\n\n// jwksCachePath returns the on-disk path for cached JWKS for the given auth domain.\nfunc jwksCachePath(authDomain url.URL) (string, error) {\n\tconfigPath, err := getConfigPath()\n\tif err != nil {\n\t\treturn \"\", err\n\t}","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/token/jwks.go#L98-L134","documentation":"This error is returned by fetchJWKS when the HTTP response body from the JWKS (JSON Web Key Set) endpoint cannot be read. The library fetches the IdP's public keys over HTTPS and reads the body with a size cap; any I/O failure (connection reset mid-body, timeout, truncated response) triggers this wrap. It preserves the underlying cause via errors.Wrap.","triggerScenarios":"Calling fetchJWKS (via getJWKSWithCache, verifyMetadataWithRetry, or token verification) when io.ReadAll on the limited response reader fails — typically the server closes the connection before the full body arrives or a proxy interrupts the transfer. Note the status was already 200 OK, so the failure is purely in body transport.","commonSituations":"Flaky corporate proxies or TLS-terminating load balancers dropping keep-alive connections; network timeouts on slow IdP endpoints; server-side errors after the 200 header (e.g. nginx worker crash mid-response); VPN or firewall interference with long-lived connections.","solutions":["Retry the token verification / JWKS fetch; the error is often transient network interruption","Verify network path to the JWKS endpoint (curl the URL and confirm a full JSON body is returned)","Check for proxy/TLS interference; set HTTP_PROXY/HTTPS_PROXY correctly or bypass the proxy for the auth domain","Inspect the wrapped cause (%v of the error) to distinguish timeout vs connection-reset and tune the HTTP client timeout accordingly"],"exampleFix":"// before: single fetch, hard failure on transient read error\nkeySet, err := fetchJWKS(ctx, jwksURL)\nif err != nil {\n\treturn err\n}\n// after: bounded retry for transient body-read failures\nvar keySet *jose.JSONWebKeySet\nfor i := 0; i < 3; i++ {\n\tkeySet, err = fetchJWKS(ctx, jwksURL)\n\tif err == nil {\n\t\tbreak\n\t}\n\ttime.Sleep(time.Duration(i+1) * time.Second)\n}","handlingStrategy":"retry","validationCode":"// probe the JWKS endpoint before verification\nresp, err := http.Get(jwksURL)\nif err == nil {\n\t_, err = io.Copy(io.Discard, resp.Body) // body must be fully readable\n\tresp.Body.Close()\n}\nif err != nil {\n\treturn fmt.Errorf(\"JWKS endpoint unreachable or body unreadable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if _, err := verifyWithRetry(ctx, 3); err != nil {\n\tvar netErr net.Error\n\tif errors.As(err, &netErr) || strings.Contains(err.Error(), \"failed to read JWKS response body\") {\n\t\t// transient: back off and retry\n\t}\n}","preventionTips":["Set a generous HTTP client timeout for IdP endpoints","Bypass flaky proxies for auth domains","Monitor wrapped cause strings for timeout vs reset patterns","Cache JWKS successfully so transient failures don't block verification"],"tags":["network","http","jwks","error-handling"],"backgroundTag":"file-read-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"}