TryGhost/Ghost · error · BadRequestError

Could not fetch user from the session.

Error message

Could not fetch user from the session.

What it means

A BadRequestError from `sendAuthCodeToUser` (the MFA/auth-code send path) when the session has no `user_id`. After CSRF checks pass, the code requires an already-assigned session user to send the auth code to; a session without a bound user cannot receive one. This guards the auth-code endpoint from being called before login completes.

Source

Thrown at ghost/core/core/server/services/auth/session/session-service.js:432

            device: deviceParts.join(', '),
            location: await getGeolocationFromIP(ip),
            time: formatTime(new Date())
        };
    }

    /**
     * sendAuthCodeToUser
     *
     * @param {Req} req
     * @param {Res} res
     * @returns {Promise<void>}
     */
    async function sendAuthCodeToUser(req, res) {
        const session = await getSession(req, res);
        cookieCsrfProtection(req, session);

        if (!session.user_id) {
            throw new BadRequestError({
                message: 'Could not fetch user from the session.'
            });
        }

        rotateAuthCodeChallenge(session);
        const token = await generateAuthCodeForUser(req, res);

        let user;
        try {
            user = await findUserById({id: session.user_id});
        } catch (error) {
            // User session likely doesn't contain a valid user ID
            throw new BadRequestError({
                message: 'Could not fetch user from the session.'
            });
        }

        const recipient = user.get('email');

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Complete the credential step (which calls `assignUserToSession`) before requesting the auth code.
  2. If the session is stale, restart the login flow to get a fresh session with a bound `user_id`.
  3. Ensure the session cookie from the credential step is sent with the auth-code request.
  4. Check that nothing between the two steps clears the cookie or rotates the session id without re-assigning the user.

Example fix

// before: requesting auth code before login assigns a user
await api.sendAuthCode(); // session.user_id missing

// after: complete credential step first, reuse its session cookie
await api.login({email, password}); // assigns user_id to session
await api.sendAuthCode(); // now bound
Defensive patterns

Strategy: validation

Validate before calling

function assertSessionHasUser(session) {
  if (!session?.user_id) {
    throw new Error('No user bound to session; complete the credential step first');
  }
}

Type guard

const sessionHasUser = (s) => Boolean(s && s.user_id);

Try / catch

try {
  await api.sendAuthCode();
} catch (err) {
  if (err.type === 'BadRequestError' && /fetch user from the session/i.test(err.message)) restartLoginFlow();
  else throw err;
}

Prevention

When it happens

Trigger: Calling the send-auth-code endpoint with a session cookie that has never had a user assigned — e.g. before the credentials have been validated, after session expiry/rotation cleared the user binding, or with a freshly created empty session cookie.

Common situations: The client requests the auth code before submitting credentials; the session expired between login and the MFA step; a cookie from a different/old session is reused; the login flow was interrupted and the session is in a half-initialized state.

Related errors


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/57b751de22b5e350. Report an issue: GitHub.