{"record":{"id":"19286a4a37286f69","repo":"ory/hydra","slug":"errunabletofindkeyid","errorCode":"ErrUnableToFindKeyID","errorMessage":"specified JWK kid can not be found in the JWK sets","messagePattern":"specified JWK kid can not be found in the JWK sets","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/jwksx/fetcher_v2.go","lineNumber":28,"sourceCode":"\n\t\"github.com/ory/herodot\"\n\n\t\"github.com/hashicorp/go-retryablehttp\"\n\n\t\"github.com/ory/x/fetcher\"\n\n\t\"go.opentelemetry.io/otel/attribute\"\n\t\"go.opentelemetry.io/otel/trace\"\n\n\t\"github.com/ory/x/otelx\"\n\n\t\"github.com/dgraph-io/ristretto/v2\"\n\t\"github.com/lestrrat-go/jwx/jwk\"\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\nvar ErrUnableToFindKeyID = errors.New(\"specified JWK kid can not be found in the JWK sets\")\n\ntype (\n\tfetcherNextOptions struct {\n\t\tforceKID   string\n\t\tcacheTTL   time.Duration\n\t\tuseCache   bool\n\t\thttpClient *retryablehttp.Client\n\t\tschemes    []string\n\t}\n\t// FetcherNext is a JWK fetcher that can be used to fetch JWKs from multiple locations.\n\tFetcherNext struct {\n\t\tcache *ristretto.Cache[[]byte, jwk.Set]\n\t}\n\t// FetcherNextOption is a functional option for the FetcherNext.\n\tFetcherNextOption func(*fetcherNextOptions)\n)\n\n// NewFetcherNext returns a new FetcherNext instance.","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/jwksx/fetcher_v2.go#L10-L46","documentation":"ErrUnableToFindKeyID is the sentinel error returned by ResolveKeyFromLocations when a JWK matching the requested kid cannot be found in any fetched JWK set. If forceKID is set and jwk.Set.LookupKeyID fails, or the set is empty so even Get(0) fails, this error (wrapped with a stack trace) is returned. It signals a key-selection failure, not a network failure.","triggerScenarios":"Calling ResolveKeyFromLocations with opts.forceKID set to a kid absent from the fetched JWKS, or with forceKID empty on a JWK set that contains zero keys (set.Get(0) not found).","commonSituations":"Tokens signed with a kid rotated out of the JWKS before old tokens expire, typos/mismatched kid between issuer and fetcher config, misconfigured JWKS URL returning an empty or wrong-tenant key set, cache serving a stale empty set.","solutions":["Log/inspect the fetched JWK set and compare its kids with the token's kid; fix the kid value or the JWKS URL.","Bypass/reduce the cache TTL so a freshly rotated key is re-fetched from the issuer.","Ensure the issuer publishes the signing key (check the JWKS endpoint directly with curl) and wait for rotation propagation.","If forceKID isn't required, drop it and fall back to the first key only when the set is known to contain exactly one key."],"exampleFix":"// before\nkey, err := fetcher.ResolveKeyFromLocations(ctx, []jwksx.Fetcher{f}, &jwksx.NextOptions{ForceKID: \"old-kid\"})\n// after\nkid := tokenFromHeader(\"kid\") // read from the actual token\nkey, err := fetcher.ResolveKeyFromLocations(ctx, []jwksx.Fetcher{f}, &jwksx.NextOptions{ForceKID: kid})","handlingStrategy":"validation","validationCode":"// before resolving, verify the kid exists in the fetched set\nset, err := f.Fetch(ctx)\nif err != nil { return err }\nif opts.ForceKID != \"\" {\n\tif _, found := set.LookupKeyID(opts.ForceKID); !found {\n\t\treturn fmt.Errorf(\"kid %q not in JWKS (available kids: %v)\", opts.ForceKID, kidsOf(set))\n\t}\n} else if set.Len() == 0 {\n\treturn fmt.Errorf(\"JWKS at %s contains no keys\", jwksURL)\n}","typeGuard":null,"tryCatchPattern":"key, err := fetcher.ResolveKeyFromLocations(ctx, locs, opts)\nif err != nil {\n\tif errors.Is(err, jwksx.ErrUnableToFindKeyID) {\n\t\t// refresh cache / re-fetch JWKS once, then retry the resolution\n\t\treturn resolveAfterRefresh(ctx, locs, opts)\n\t}\n\treturn err\n}","preventionTips":["Always take the kid from the token header rather than hardcoding forceKID","Keep JWKS cache TTL well below key rotation intervals and implement refresh-on-miss","Monitor issuer key rotation and ensure old keys stay published during overlap windows","Alert on empty JWKS responses from the issuer"],"tags":["jwks","jwt","key-lookup"],"backgroundTag":"jwk-kid-not-found","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}