ory/hydra · error

key not found

Error message

key not found

What it means

jwk.FindPublicKey filters a JOSE key set down to its public keys via ExcludePrivateKeys and errors when nothing remains. It means the provided JSONWebKeySet contains no public key to use — typically for verifying a signature. The library throws it rather than silently returning a nil key.

Source

Thrown at jwk/helper.go:146

		r.Logger().Warnf("JSON Web Key Set %q does not exist yet, generating new key pair...", set)
	default:
		return nil, err
	}

	return r.KeyManager().GenerateAndPersistKeySet(ctx, set, kid, alg, use)
}

func First(keys []jose.JSONWebKey) *jose.JSONWebKey {
	if len(keys) == 0 {
		return nil
	}
	return &keys[0]
}

func FindPublicKey(set *jose.JSONWebKeySet) (key *jose.JSONWebKey, err error) {
	keys := ExcludePrivateKeys(set)
	if len(keys.Keys) == 0 {
		return nil, errors.New("key not found")
	}

	return First(keys.Keys), nil
}

func FindPrivateKey(set *jose.JSONWebKeySet) (key *jose.JSONWebKey, err error) {
	keys := ExcludePublicKeys(set)
	if len(keys.Keys) == 0 {
		return nil, errors.New("key not found")
	}

	return First(keys.Keys), nil
}

func ExcludePublicKeys(set *jose.JSONWebKeySet) *jose.JSONWebKeySet {
	keys := new(jose.JSONWebKeySet)
	for _, k := range set.Keys {
		if !k.IsPublic() {

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Verify the JWKS source actually returns at least one public key (`curl` the jwks_uri and inspect `keys`).
  2. Regenerate/republish the key set including the public component of the signing key.
  3. Check key filtering logic upstream — ensure the set passed in is not pre-stripped of public keys.
  4. Refresh the cached key set after rotation so the new public key is present.

Example fix

// before
key, err := jwk.FindPublicKey(&jose.JSONWebKeySet{})
// after: ensure the set has public keys first
set := fetchJWKS(ctx, jwksURI)
if len(jwk.ExcludePrivateKeys(set).Keys) == 0 {
    return fmt.Errorf("no public keys at %s", jwksURI)
}
key, err := jwk.FindPublicKey(set)
Defensive patterns

Strategy: type-guard

Validate before calling

// check the set has public keys before calling FindPublicKey
if len(jwk.ExcludePrivateKeys(set).Keys) == 0 {
    return nil, fmt.Errorf("JWKS from %s has no public keys", uri)
}

Type guard

func hasPublicKeys(set *jose.JSONWebKeySet) bool {
    return set != nil && len(jwk.ExcludePrivateKeys(set).Keys) > 0
}

Try / catch

// treat as a fetch/cache problem and refresh
key, err := jwk.FindPublicKey(set)
if err != nil && err.Error() == "key not found" {
    set = refreshJWKS(ctx, uri)
    key, err = jwk.FindPublicKey(set)
}

Prevention

When it happens

Trigger: Calling FindPublicKey with a set that is empty, or that contains only private keys (which are excluded), or only keys filtered out by other means — returning `errors.New("key not found")` at jwk/helper.go:146.

Common situations: The JWKS endpoint returned an empty `keys` array; keys were rotated and the cached set has none matching; someone published a JWKS containing only private JWKs by mistake; a test fixture loads keys from the wrong file.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/7192ebad215b8b47. Report an issue: GitHub.