ory/hydra · error

GetTokenClaims() must not be nil

Error message

GetTokenClaims() must not be nil

What it means

After a successful JWTSessionContainer assertion, generate() calls GetJWTClaims() and refuses to proceed if it returns nil. A JWT session without a claims object cannot produce a valid token payload, so fosite fails fast with this error.

Source

Thrown at fosite/handler/oauth2/strategy_jwt.go:92

	case v.Has(jwt.ValidationErrorExpired):
		return fosite.ErrTokenExpired
	case v.Has(jwt.ValidationErrorAudience |
		jwt.ValidationErrorIssuedAt |
		jwt.ValidationErrorIssuer |
		jwt.ValidationErrorNotValidYet |
		jwt.ValidationErrorId |
		jwt.ValidationErrorClaimsInvalid):
		return fosite.ErrTokenClaim
	default:
		return fosite.ErrRequestUnauthorized
	}
}

func (h *DefaultJWTStrategy) generate(ctx context.Context, tokenType fosite.TokenType, requester fosite.Requester) (string, string, error) {
	if jwtSession, ok := requester.GetSession().(JWTSessionContainer); !ok {
		return "", "", errors.Errorf("Session must be of type JWTSessionContainer but got type: %T", requester.GetSession())
	} else if claims := jwtSession.GetJWTClaims(); claims == nil {
		return "", "", errors.New("GetTokenClaims() must not be nil")
	} else {
		claims.
			With(
				jwtSession.GetExpiresAt(tokenType),
				requester.GetGrantedScopes(),
				requester.GetGrantedAudience(),
			).
			WithDefaults(
				time.Now().UTC(),
				h.Config.GetAccessTokenIssuer(ctx),
			).
			WithScopeField(
				h.Config.GetJWTScopeField(ctx),
			)

		return h.Signer.Generate(ctx, claims.ToMapClaims(), jwtSession.GetJWTHeader())
	}
}

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Initialize the claims object in your session constructor: sess.GetJWTClaims().With(...) or ensure GetJWTClaims() always returns a non-nil *jwt.JWTClaims
  2. Fix custom GetJWTClaims() to lazily create the claims struct if nil instead of returning nil
  3. Verify serialization/deserialization of sessions preserves the claims object
  4. Check that storage round-trips (cache, DB) don't strip claims from the session

Example fix

// before
func (s *MySession) GetJWTClaims() jwt.JWTClaimsContainer { return s.claims } // nil if unset
// after
func (s *MySession) GetJWTClaims() jwt.JWTClaimsContainer {
    if s.claims == nil { s.claims = &jwt.JWTClaims{} }
    return s.claims
}
Defensive patterns

Strategy: type-guard

Validate before calling

if js, ok := req.GetSession().(JWTSessionContainer); ok && js.GetJWTClaims() == nil {
    return errors.New("session has nil JWT claims; initialize claims before token generation")
}

Type guard

func hasJWTClaims(s fosite.Session) bool {
    js, ok := s.(JWTSessionContainer)
    return ok && js.GetJWTClaims() != nil
}

Try / catch

resp, err := oauth2Client.GetAccessToken(ctx, req)
if err != nil && strings.Contains(err.Error(), "GetTokenClaims() must not be nil") {
    log.Fatalf("session misconfigured: claims were nil: %v", err)
}

Prevention

When it happens

Trigger: Calling GenerateAccessToken with a session whose GetJWTClaims() returns nil — e.g. a JWTSessionContainer implementation that never initialized its claims field, or a session deserialized from storage with claims lost.

Common situations: Custom JWTSessionContainer implementations that lazily allocate claims but were constructed without them; copying session structs so the pointer to claims was nil; sessions restored from JSON where claims were omitted.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/8e3fd9f355b2dd75. Report an issue: GitHub.