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

  1. Retry the token verification / JWKS fetch; the error is often transient network interruption
  2. Verify network path to the JWKS endpoint (curl the URL and confirm a full JSON body is returned)
  3. Check for proxy/TLS interference; set HTTP_PROXY/HTTPS_PROXY correctly or bypass the proxy for the auth domain
  4. 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

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


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/f5cf61f47ed2283e. Report an issue: GitHub.