ory/hydra · error

flow Subject %s does not match the HandledLoginRequest Subje

Error message

flow Subject %s does not match the HandledLoginRequest Subject %s

What it means

Flow.HandleLoginRequest rejects a HandledLoginRequest whose Subject differs from the Subject already stored on the Flow. Hydra flows pin the authenticated user at login initialization; accepting a different subject later would let the same flow be reused to impersonate another user, so the mismatch is treated as a hard error.

Source

Thrown at flow/flow.go:310

}

// InvalidateDeviceRequest shifts the flow state to DeviceFlowStateUsed. This
// transition is executed upon device completion.
func (f *Flow) InvalidateDeviceRequest() error {
	if err := f.State.IsAny(DeviceFlowStateUnused); err != nil {
		return err
	}
	f.State = DeviceFlowStateUsed
	return nil
}

func (f *Flow) HandleLoginRequest(h *HandledLoginRequest) error {
	if err := f.State.IsAny(FlowStateLoginInitialized, FlowStateLoginUnused, FlowStateLoginError); err != nil {
		return err
	}

	if f.Subject != "" && h.Subject != "" && f.Subject != h.Subject {
		return errors.Errorf("flow Subject %s does not match the HandledLoginRequest Subject %s", f.Subject, h.Subject)
	}

	if f.ForceSubjectIdentifier != "" && h.ForceSubjectIdentifier != "" && f.ForceSubjectIdentifier != h.ForceSubjectIdentifier {
		return errors.Errorf("flow ForceSubjectIdentifier %s does not match the HandledLoginRequest ForceSubjectIdentifier %s", f.ForceSubjectIdentifier, h.ForceSubjectIdentifier)
	}

	f.State = FlowStateLoginUnused

	if f.Context != nil {
		f.Context = h.Context
	}

	f.Subject = h.Subject
	f.ForceSubjectIdentifier = h.ForceSubjectIdentifier

	f.IdentityProviderSessionID = sqlxx.NullString(h.IdentityProviderSessionID)
	f.LoginRemember = h.Remember
	f.LoginRememberFor = h.RememberFor

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Ensure the subject that accepted the login equals the flow's stored Subject before calling HandleLoginRequest; abort and re-initialize the flow if the user changed
  2. Fetch the flow fresh from storage and re-run the login provider so the HandledLoginRequest reflects the current authenticated subject
  3. If the user genuinely changed, create a new login flow (new challenge) instead of reusing the old one
  4. Check that you are not passing a HandledLoginRequest belonging to another flow (mixed-up objects/keys)

Example fix

// before
handled := &flow.HandledLoginRequest{Subject: currentUser.Subject, Context: ...}
err := loginFlow.HandleLoginRequest(handled) // panics/errors if subject differs
// after
if loginFlow.Subject != "" && currentUser.Subject != loginFlow.Subject {
    loginFlow, err = newLoginFlowFor(currentUser) // create a fresh flow
    if err != nil { return err }
}
err := loginFlow.HandleLoginRequest(handled)
Defensive patterns

Strategy: validation

Validate before calling

if loginFlow.Subject != "" && handled.Subject != "" && loginFlow.Subject != handled.Subject {
    return fmt.Errorf("flow subject %s != handled subject %s; re-initialize login flow", loginFlow.Subject, handled.Subject)
}
err := loginFlow.HandleLoginRequest(handled)

Try / catch

if err := flow.HandleLoginRequest(handled); err != nil {
    if strings.Contains(err.Error(), "does not match the HandledLoginRequest Subject") {
        flow, err = reinitializeLoginFlow(authRequest)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Flow.HandleLoginRequest (public) when both f.Subject and h.Subject are non-empty and unequal — e.g. the login flow was initialized/claimed for user A but the LoginRequestHandler resolved user B, or the flow was fetched twice and completed with a different login session.

Common situations: Reusing a stale login challenge after the user logged out and in as a different account; cookie/session swapping between flow creation and login acceptance; multiple browser tabs completing the same flow with different identities; replaying a saved HandledLoginRequest object from a previous authentication.

Related errors


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