ory/hydra · error
unsupported private key type: %T
Error message
unsupported private key type: %T
What it means
jwt.DefaultSigner.Generate reached the outermost 'default' branch of its key type switch: the private key returned by GetPrivateKey is none of the recognized shapes (ECDSA/RSA/ed25519 key, JSONWebKey, or KeyPair). This is the top-level 'unsupported private key type' variant, distinct from the key-pair variant.
Source
Thrown at fosite/token/jwt/jwt.go:78
case *rsa.PrivateKey:
alg := jose.RS256
if len(t.Algs()) > 0 {
alg = t.Algs()[0]
}
return generateToken(claims, header, alg, t)
case *ecdsa.PrivateKey:
alg := jose.ES256
if len(t.Algs()) > 0 {
alg = t.Algs()[0]
}
return generateToken(claims, header, alg, t)
default:
return "", "", errors.Errorf("unsupported private / public key pairs: %T, %T", t, tt)
}
default:
return "", "", errors.Errorf("unsupported private key type: %T", t)
}
}
// Validate validates a token and returns its signature or an error if the token is not valid.
func (j *DefaultSigner) Validate(ctx context.Context, token string) (string, error) {
key, err := j.GetPrivateKey(ctx)
if err != nil {
return "", err
}
if t, ok := key.(*jose.JSONWebKey); ok {
key = t.Key
}
switch t := key.(type) {
case *rsa.PrivateKey:
return validateToken(token, t.PublicKey)
case *ecdsa.PrivateKey:View on GitHub (pinned to 4174065ffb)
Solutions
- Return a concrete supported key type (*rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey, or *jose.JSONWebKey wrapping one) from GetPrivateKey
- For symmetric needs, use HMAC signing instead of the JWT RS/ES/Ed signers (oct keys are not valid here)
- Parse keys with x509/jose before handing them to the signer and add a %T assertion/log
- Check that the configured JWK set actually contains signing (asymmetric) keys, not encryption keys
Example fix
// before
func (s *Store) GetPrivateKey(ctx) (interface{}, error) { return s.rawPEM, nil }
// after
func (s *Store) GetPrivateKey(ctx) (interface{}, error) {
block, _ := pem.Decode(s.rawPEM)
return x509.ParseECPrivateKey(block.Bytes)
} Defensive patterns
Strategy: type-guard
Validate before calling
key, err := store.GetPrivateKey(ctx)
if err != nil { return err }
switch key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey, *jose.JSONWebKey:
// ok
default:
return fmt.Errorf("GetPrivateKey returned unsupported type %T", key)
} Type guard
func isJWTPrivateKey(key interface{}) bool {
switch key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey, *jose.JSONWebKey:
return true
}
return false
} Try / catch
token, sig, err := signer.Generate(ctx, claims, header)
if err != nil && strings.Contains(err.Error(), "unsupported private key type") {
log.Fatalf("configured key %T cannot sign JWTs", key)
} Prevention
- Never return raw bytes/PEM strings from GetPrivateKey; return parsed key types
- Verify JWKS entries are asymmetric signing keys (not oct/encryption keys) before use
- Add a smoke test that signs a token at service startup
- Pin key algorithm (RS256/ES256/EdDSA) and load matching key types
When it happens
Trigger: Calling Generate when GetPrivateKey(ctx) yields something entirely unexpected — e.g. a []byte, pem string, *jose.JSONWebKey whose Key itself is an interface{} the inner switch also rejected, or nil/mis-typed custom signer key.
Common situations: Custom GetPrivateKey implementations returning raw key material instead of parsed Go key types; configuration loading a JWK file where the key type field maps to an unhandled algorithm (e.g. oct keys used with asymmetric signing); refactors that changed the key type passed to NewSigner.
Related errors
- unsupported private / public key pairs: %T, %T
- header, body and signature must all be set
- Token is expired
- Session must be of type JWTSessionContainer but got type: %T
- GetTokenClaims() must not be nil
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/3d7df2a63a61dca8.
Report an issue: GitHub.