Tencent/WeKnora · error
JWKS document contains no keys
Error message
JWKS document contains no keys
What it means
fetchOIDCJWKS successfully fetched and decoded the JWKS document (within a 1MB limit), but the top-level keys array was empty. A JWKS with no keys cannot verify any token, so this is treated as a hard failure.
Source
Thrown at internal/application/service/user.go:1928
}
req.Header.Set("Accept", "application/json")
resp, err := newOIDCHTTPClient().Do(req)
if err != nil {
return nil, err
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 2048))
return nil, fmt.Errorf("JWKS request failed: status=%d", resp.StatusCode)
}
var jwks oidcJWKS
if err := json.NewDecoder(io.LimitReader(resp.Body, 1<<20)).Decode(&jwks); err != nil {
return nil, fmt.Errorf("failed to decode JWKS document: %w", err)
}
if len(jwks.Keys) == 0 {
return nil, errors.New("JWKS document contains no keys")
}
return &jwks, nil
}
const oidcIDTokenLeeway = 2 * time.Minute
// verifyOIDCIDToken cryptographically verifies an OIDC id_token: it checks the
// RSA signature against the provider's JWKS (matched by kid) and validates the
// issuer, audience (client_id), expiry and subject. It returns the verified claims.
func (s *userService) verifyOIDCIDToken(
ctx context.Context, cfg *config.OIDCAuthConfig, idToken string,
) (map[string]interface{}, error) {
if strings.TrimSpace(cfg.JwksURI) == "" {
return nil, errors.New("cannot verify OIDC id_token: no jwks_uri configured")
}
if strings.TrimSpace(cfg.IssuerURL) == "" {
return nil, errors.New("cannot verify OIDC id_token: issuer is not configured")
}View on GitHub (pinned to 988cbb0330)
Solutions
- Point JwksURI at the provider's real JWKS (from discovery metadata).
- Check the provider's key management — ensure at least one active signing key exists.
- If using a stub/mock for tests, populate the keys array with a valid RSA JWK.
- Retry later if the provider is mid-rotation with a temporarily empty key set.
Example fix
// before (stub response)
{"keys": []}
// after
{"keys": [{"kty":"RSA","kid":"k1","use":"sig","n":"...","e":"AQAB"}]} Defensive patterns
Strategy: validation
Validate before calling
var doc struct{ Keys []json.RawMessage `json:"keys"` }
json.Unmarshal(body, &doc)
if len(doc.Keys) == 0 {
return fmt.Errorf("jwks_uri %s returned an empty key set", cfg.JwksURI)
} Type guard
func jwksHasKeys(body []byte) bool {
var d struct{ Keys []json.RawMessage `json:"keys"` }
return json.Unmarshal(body, &d) == nil && len(d.Keys) > 0
} Prevention
- Verify jwks_uri returns a populated key set during setup (curl + jq '.keys | length').
- Ensure the IdP has at least one active signing key.
- Populate test/stub JWKS with a valid RSA key.
When it happens
Trigger: GET jwks_uri returns valid JSON like {"keys": []} — json decode succeeds, len(jwks.Keys)==0.
Common situations: Provider has revoked/deleted all signing keys; jwks_uri points at an empty placeholder document; a mock/stub server returns an empty JWKS; fresh deployment where keys haven't been generated yet.
Related errors
- empty JWK modulus or exponent
- invalid JWK exponent value
- no matching JWKS key for id_token
- cannot verify OIDC id_token: issuer is not configured
- cannot verify OIDC id_token: client_id is not configured
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/c935d5b7166a1334.
Report an issue: GitHub.