ory/hydra · error
expected status code 200 but got
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.
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
- Failed to read response
- a key ID must be specified when multiple JWK sets are…
- can not serve request over insecure http
- could not start debug server on port
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/9147bba52d3a36b4.
Report an issue: GitHub.
Appendix: 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)