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
- Complete the credential step (which calls `assignUserToSession`) before requesting the auth code.
- If the session is stale, restart the login flow to get a fresh session with a bound `user_id`.
- Ensure the session cookie from the credential step is sent with the auth-code request.
- 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
- Call the auth-code endpoint only after credentials are validated and the session has a user_id.
- Reuse the session cookie from the credential step on subsequent auth requests.
- Treat session expiry as a trigger to restart the login flow.
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
- Could not determine origin of request. Please ensure an Orig
- Request made from incorrect origin. Expected '${adminOrigin}
- Request made from incorrect origin. Expected '${session.orig
- Download failed: ${response.status} ${response.statusText}
- Failed to update newsletter
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/57b751de22b5e350.
Report an issue: GitHub.