cloudflare/cloudflared · error
failed to read JWKS response body
Error message
failed to read JWKS response body
What it means
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.
Source
Thrown at token/jwks.go:116
client := &http.Client{
CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
return http.ErrUseLastResponse
},
Timeout: time.Second * 10,
}
resp, err := client.Get(jwksURL.String()) // nolint: gosec
if err != nil {
return nil, errors.Wrapf(err, "failed to fetch JWKS from %s", jwksURL.String())
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("JWKS endpoint %s returned status %d", jwksURL.String(), resp.StatusCode)
}
body, err := io.ReadAll(io.LimitReader(resp.Body, maxJWKSResponseSize+1))
if err != nil {
return nil, errors.Wrap(err, "failed to read JWKS response body")
}
if len(body) > maxJWKSResponseSize {
return nil, fmt.Errorf("JWKS response body exceeds %d bytes", maxJWKSResponseSize)
}
var keySet jose.JSONWebKeySet
if err := json.Unmarshal(body, &keySet); err != nil {
return nil, errors.Wrap(err, "failed to parse JWKS")
}
return &keySet, nil
}
// jwksCachePath returns the on-disk path for cached JWKS for the given auth domain.
func jwksCachePath(authDomain url.URL) (string, error) {
configPath, err := getConfigPath()
if err != nil {
return "", err
}View on GitHub (pinned to 2253eeeb25)
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
Example fix
// before: single fetch, hard failure on transient read error
keySet, err := fetchJWKS(ctx, jwksURL)
if err != nil {
return err
}
// after: bounded retry for transient body-read failures
var keySet *jose.JSONWebKeySet
for i := 0; i < 3; i++ {
keySet, err = fetchJWKS(ctx, jwksURL)
if err == nil {
break
}
time.Sleep(time.Duration(i+1) * time.Second)
} Defensive patterns
Strategy: retry
Validate before calling
// probe the JWKS endpoint before verification
resp, err := http.Get(jwksURL)
if err == nil {
_, err = io.Copy(io.Discard, resp.Body) // body must be fully readable
resp.Body.Close()
}
if err != nil {
return fmt.Errorf("JWKS endpoint unreachable or body unreadable: %w", err)
} Try / catch
if _, err := verifyWithRetry(ctx, 3); err != nil {
var netErr net.Error
if errors.As(err, &netErr) || strings.Contains(err.Error(), "failed to read JWKS response body") {
// transient: back off and retry
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- failed to fetch JWKS from %s
- quick tunnel provisioning failed with status %d: %s
- quick tunnel provisioning failed: %s
- unable to check for update: %d
- failed to find Access application at %s
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/f5cf61f47ed2283e.
Report an issue: GitHub.