ory/hydra · error

Expected request to be of type *Session, but got: %T

Error message

Expected request to be of type *Session, but got: %T

What it means

sqlDeviceSchemaFromRequest converts a fosite Requester into a database row and expects the attached session to be of the concrete type *oauth2.Session (so it can read ConsentChallenge). If GetSession() returns a non-nil value of any other Go type, it returns this error with the actual %T type name. It indicates that a custom session implementation was plugged into the OAuth2 flow but the SQL persister cannot serialize it for the device code flow.

Solutions

  1. Make the session passed into the device flow an instance of *oauth2.Session (or embed and convert it before calling the persister).
  2. If using a custom session for claim extension, keep the consent challenge accessible by wrapping/converting to *oauth2.Session before persistence.
  3. Check config for custom session/claim extensions (e.g. oauth2.session) and remove or adapt them when using the device code flow.
  4. Fix test/integration harnesses to construct requests with the correct session type.

Example fix

// before
req.SetSession(&myCustomSession{Subject: "user"})
// after
sess := oauth2.NewSession("client-id")
sess.Subject = "user"
req.SetSession(sess)
Defensive patterns

Strategy: type-guard

Type guard

func isOAuth2Session(r fosite.Requester) bool {
    _, ok := r.GetSession().(*oauth2.Session)
    return ok
}
// call before invoking CreateDeviceAuthSession

Prevention

When it happens

Trigger: Registering a custom session type via config (oauth2.session.establish_session or a custom SessionStorage/transform extension) and then calling CreateDeviceAuthSession or UpdateDeviceCodeSessionBySignature with that request; running device authorization flow while a custom JWT/session claims extension swapped the session type.

Common situations: Projects that customize access/id token claims with their own session struct but later enable the device flow; integration code that constructs a fosite request manually with the wrong session type; tests passing a mock session into the persister.

Related errors


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

Appendix: source

Thrown at persistence/sql/persister_device.go:128

	}

	session, err := json.Marshal(r.GetSession())
	if err != nil {
		return nil, errors.WithStack(err)
	}

	if p.r.Config().EncryptSessionData(ctx) {
		ciphertext, err := p.r.KeyCipher().Encrypt(ctx, session, nil)
		if err != nil {
			return nil, errors.WithStack(err)
		}
		session = []byte(ciphertext)
	}

	var challenge sql.NullString
	rr, ok := r.GetSession().(*oauth2.Session)
	if !ok && r.GetSession() != nil {
		return nil, errors.Errorf("Expected request to be of type *Session, but got: %T", r.GetSession())
	} else if ok {
		if len(rr.ConsentChallenge) > 0 {
			challenge = sql.NullString{Valid: true, String: rr.ConsentChallenge}
		}
	}

	return &DeviceRequestSQL{
		Request:           r.GetID(),
		ConsentChallenge:  challenge,
		ID:                deviceCodeSignature,
		UserCodeID:        userCodeSignature,
		RequestedAt:       r.GetRequestedAt(),
		InternalExpiresAt: sqlxx.NullTime(expiresAt),
		Client:            r.GetClient().GetID(),
		Scopes:            strings.Join(r.GetRequestedScopes(), "|"),
		GrantedScope:      strings.Join(r.GetGrantedScopes(), "|"),
		GrantedAudience:   strings.Join(r.GetGrantedAudience(), "|"),
		RequestedAudience: strings.Join(r.GetRequestedAudience(), "|"),

View on GitHub (pinned to 4174065ffb)