dgraph-io/dgraph · error

not enough JWKUrls

Error message

not enough JWKUrls

What it means

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.

Source

Thrown at graphql/authorization/auth.go:445

// the fetching of key is failed even for one of the JWKUrl.
func (a *AuthMeta) FetchJWKs() error {
	if len(a.JWKUrls) == 0 {
		return errors.Errorf("No JWKUrl supplied")
	}

	for i := range a.JWKUrls {
		err := a.FetchJWK(i)
		if err != nil {
			return err
		}
	}
	return nil
}

// FetchJWK fetches the JSON web Key set for the JWKUrl at a given index.
func (a *AuthMeta) FetchJWK(i int) error {
	if len(a.JWKUrls) <= i {
		return errors.Errorf("not enough JWKUrls")
	}

	req, err := http.NewRequest("GET", a.JWKUrls[i], nil)
	if err != nil {
		return err
	}

	resp, err := a.httpClient.Do(req)
	if err != nil {
		return err
	}
	defer func() {
		if err := resp.Body.Close(); err != nil {
			glog.Warningf("error closing body: %v", err)
		}
	}()

	data, err := io.ReadAll(resp.Body)

View on GitHub (pinned to 759e242be6)

Solutions

  1. Check that JWKUrls is not being mutated (shrunk) concurrently with refreshJWK/FetchJWK — synchronize config reloads.
  2. Only call FetchJWK with indices obtained by ranging over the current a.JWKUrls.
  3. Re-fetch/rebuild AuthMeta atomically when JWK configuration changes instead of mutating the slice in place.
  4. Add a bounds debug log: length of JWKUrls vs requested index to find the offending caller.
  5. Refresh loop should re-read len(a.JWKUrls) each iteration rather than caching an old count.

Example fix

// before
for i := 0; i < oldCount; i++ { a.FetchJWK(i) }
// after
for i := range a.JWKUrls { a.FetchJWK(i) }
Defensive patterns

Strategy: validation

Validate before calling

func safeFetchJWK(a *authorization.AuthMeta, i int) error {
    if a == nil || i < 0 || i >= len(a.JWKUrls) {
        return fmt.Errorf("index %d out of range for %d JWKUrls", i, len(a.JWKUrls))
    }
    return a.FetchJWK(i)
}

Type guard

func inRange(i, n int) bool { return i >= 0 && i < n }

Try / catch

if err := a.FetchJWK(i); err != nil && strings.Contains(err.Error(), "not enough JWKUrls") {
    log.Printf("JWK fetch skipped: index %d, len(JWKUrls)=%d", i, len(a.JWKUrls))
    return nil // or re-sync config and retry
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of dgraph-io/dgraph@759e242be6 (2026-09-01). Data as JSON: /api/errors/c10797332c8d9f19. Report an issue: GitHub.