{"record":{"id":"c10797332c8d9f19","repo":"dgraph-io/dgraph","slug":"not-enough-jwkurls","errorCode":null,"errorMessage":"not enough JWKUrls","messagePattern":"not enough JWKUrls","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"graphql/authorization/auth.go","lineNumber":445,"sourceCode":"// the fetching of key is failed even for one of the JWKUrl.\nfunc (a *AuthMeta) FetchJWKs() error {\n\tif len(a.JWKUrls) == 0 {\n\t\treturn errors.Errorf(\"No JWKUrl supplied\")\n\t}\n\n\tfor i := range a.JWKUrls {\n\t\terr := a.FetchJWK(i)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// FetchJWK fetches the JSON web Key set for the JWKUrl at a given index.\nfunc (a *AuthMeta) FetchJWK(i int) error {\n\tif len(a.JWKUrls) <= i {\n\t\treturn errors.Errorf(\"not enough JWKUrls\")\n\t}\n\n\treq, err := http.NewRequest(\"GET\", a.JWKUrls[i], nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tresp, err := a.httpClient.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() {\n\t\tif err := resp.Body.Close(); err != nil {\n\t\t\tglog.Warningf(\"error closing body: %v\", err)\n\t\t}\n\t}()\n\n\tdata, err := io.ReadAll(resp.Body)","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/dgraph-io/dgraph/blob/759e242be62c91f8d084da06ad0c8d21256d9c07/graphql/authorization/auth.go#L427-L463","documentation":"FetchJWK(i) fetches the key set for JWKUrls[i], and guards against the index being out of range. If i is beyond the configured JWKUrls, the slice has fewer entries than expected — an internal invariant violation rather than a network problem. It is raised before any HTTP request is made.","triggerScenarios":"FetchJWK (called by FetchJWKs or refreshJWK) receives i >= len(a.JWKUrls) — e.g. refreshJWK invoked with an index computed against an older, longer JWKUrls list that was later shrunk, or a bad index passed directly.","commonSituations":"JWKUrls list mutated/reconfigured at runtime (shorter list) while background refresh still iterates old indices; off-by-one or loop bug in custom code calling FetchJWK directly; concurrent config reload racing a refresh.","solutions":["Check that JWKUrls is not being mutated (shrunk) concurrently with refreshJWK/FetchJWK — synchronize config reloads.","Only call FetchJWK with indices obtained by ranging over the current a.JWKUrls.","Re-fetch/rebuild AuthMeta atomically when JWK configuration changes instead of mutating the slice in place.","Add a bounds debug log: length of JWKUrls vs requested index to find the offending caller.","Refresh loop should re-read len(a.JWKUrls) each iteration rather than caching an old count."],"exampleFix":"// before\nfor i := 0; i < oldCount; i++ { a.FetchJWK(i) }\n// after\nfor i := range a.JWKUrls { a.FetchJWK(i) }","handlingStrategy":"validation","validationCode":"func safeFetchJWK(a *authorization.AuthMeta, i int) error {\n    if a == nil || i < 0 || i >= len(a.JWKUrls) {\n        return fmt.Errorf(\"index %d out of range for %d JWKUrls\", i, len(a.JWKUrls))\n    }\n    return a.FetchJWK(i)\n}","typeGuard":"func inRange(i, n int) bool { return i >= 0 && i < n }","tryCatchPattern":"if err := a.FetchJWK(i); err != nil && strings.Contains(err.Error(), \"not enough JWKUrls\") {\n    log.Printf(\"JWK fetch skipped: index %d, len(JWKUrls)=%d\", i, len(a.JWKUrls))\n    return nil // or re-sync config and retry\n}","preventionTips":["Iterate with `for i := range a.JWKUrls` instead of cached lengths","Guard config reloads with a mutex so JWKUrls is not shrunk mid-refresh","Rebuild AuthMeta atomically on config change","Range-check indices before calling FetchJWK directly"],"tags":["jwk","index-out-of-range","configuration","concurrency"],"backgroundTag":"index-out-of-range","analyzedSha":"759e242be62c91f8d084da06ad0c8d21256d9c07","analyzedAt":"2026-09-01T14:42:12.034Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}