ory/hydra · error
expected status code 200 but got %d when requesting %s
Error message
expected status code 200 but got %d when requesting %s
What it means
Fetcher.GetKey fetches a JWKS document from f.remote over plain HTTP GET and requires HTTP 200. Any other status (404 wrong path, 401/403 auth required, 500 server error, 301/302 if redirects are not followed) aborts with this error instead of attempting to parse the body. It reports both the received status code and the requested URL.
Source
Thrown at oryx/jwksx/fetcher.go:53
// GetKey retrieves a JSON Web Key from the cache, fetches it from a remote if it is not yet cached or returns an error.
//
// DEPRECATED: Use FetcherNext instead.
func (f *Fetcher) GetKey(kid string) (*jose.JSONWebKey, error) {
f.RLock()
if k, ok := f.keys[kid]; ok {
f.RUnlock()
return &k, nil
}
f.RUnlock()
res, err := f.c.Get(f.remote)
if err != nil {
return nil, errors.WithStack(err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, errors.Errorf("expected status code 200 but got %d when requesting %s", res.StatusCode, f.remote)
}
var set jose.JSONWebKeySet
if err := json.NewDecoder(res.Body).Decode(&set); err != nil {
return nil, errors.WithStack(err)
}
for _, k := range set.Keys {
f.Lock()
f.keys[k.KeyID] = k
f.Unlock()
}
f.RLock()
defer f.RUnlock()
if k, ok := f.keys[kid]; ok {
return &k, nil
}View on GitHub (pinned to 4174065ffb)
Solutions
- Verify the remote URL is the correct, complete JWKS endpoint (usually issuer + '/.well-known/jwks.json' or from the OpenID discovery 'jwks_uri').
- Check server logs / curl the URL to see the actual status and response body for the real cause (401 vs 404 vs 500).
- If the endpoint requires auth or is behind a proxy, configure an http.Client with the needed transport before constructing the fetcher.
- Implement retry with backoff for transient 5xx responses, and cache keys so a brief outage does not break verification.
Example fix
// before
f := jwksx.NewFetcher("https://issuer.example.com/jwks") // 404
// after
f := jwksx.NewFetcher("https://issuer.example.com/.well-known/jwks.json") Defensive patterns
Strategy: retry
Validate before calling
resp, err := http.Head(jwksURL)
if err != nil || resp.StatusCode != 200 {
return fmt.Errorf("JWKS URL %s not healthy (status %v)", jwksURL, statusOrNil(resp))
} Try / catch
key, err := fetcher.GetKey(kid)
if err != nil && strings.Contains(err.Error(), "expected status code 200") {
time.Sleep(backoff)
key, err = fetcher.GetKey(kid) // retry transient 5xx
} Prevention
- Point the fetcher at the exact jwks_uri from OpenID discovery.
- Smoke-test the JWKS URL on deploy; alert on non-200.
- Cache keys to tolerate brief JWKS endpoint outages.
When it happens
Trigger: Calling jwksx.NewFetcher(remote).GetKey(kid) where the HTTP GET of the JWKS URL (oryx/jwksx/fetcher.go:53) returns a non-200 status — first fetch or any cache-miss fetch.
Common situations: Misconfigured issuer/JWKS URL (404), the JWKS endpoint requiring auth (401/403), the identity provider being down (5xx), or pointing at an HTML page instead of a JWKS endpoint.
Understand the failure class
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- Failed to accept user code request: %s
- Failed to read response: %s
- key not found
- errKeyNotFound
- DNS lookup timed out
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/9147bba52d3a36b4.
Report an issue: GitHub.