HeyPuter/puter · error · HttpError
user_not_found
user_not_found
Error message
User not found.
What it means
Thrown by POST /send-confirm-email (HTTP 404, legacyCode 'user_not_found') when the authenticated actor's user row cannot be loaded by id (this.stores.user.getById with force:true bypasses cache and still returns null). The route requires a user actor (requireUserActor:true) and allows unconfirmed actors, so reaching here means the session token resolved to a user id that no longer exists in the DB — a delete/race condition, not a normal client error.
Source
Thrown at src/backend/controllers/auth/AuthController.ts:1195
// -- Email confirmation ------------------------------------------
@Post('/send-confirm-email', {
subdomain: ['api', ''],
requireUserActor: true,
allowUnconfirmed: true,
rateLimit: {
scope: 'send-confirm-email',
limit: 10,
window: 60 * 60_000,
key: 'user',
},
})
async handleSendConfirmEmail(req: Request, res: Response): Promise<void> {
const user = await this.stores.user.getById(req.actor!.user.id!, {
force: true,
});
if (!user)
throw new HttpError(404, 'User not found.', {
legacyCode: 'user_not_found' as never,
});
if (user.suspended)
throw new HttpError(403, 'Account suspended.', {
legacyCode: 'account_suspended',
});
if (!user.email)
throw new HttpError(400, 'No email on file.', {
legacyCode: 'bad_request',
});
const code = String(crypto.randomInt(100000, 1000000));
await this.stores.user.update(user.id, {
email_confirm_code: code,
});
if (this.clients.email) {
try {View on GitHub (pinned to 908ec23eda)
Solutions
- Treat a 404 user_not_found on an authenticated endpoint as an invalid session: clear local auth state and send the user to login/signup.
- If reproducible only under load, check for replication lag between the write and read databases.
- Confirm the account wasn't concurrently deleted (check admin/delete-user logs).
Example fix
// before
await post('/send-confirm-email');
// after
try {
await post('/send-confirm-email');
} catch (e) {
if (e.statusCode === 404 && e.legacyCode === 'user_not_found') {
clearSession();
navigateToLogin();
} else throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// not a client-input error; pre-check by validating the session is still alive
const me = await getMe();
if (!me) { clearSession(); navigateToLogin(); return; } Try / catch
try {
await post('/send-confirm-email');
} catch (e) {
if (e.statusCode === 404 && e.legacyCode === 'user_not_found') {
clearSession();
navigateToLogin();
} else throw e;
} Prevention
- Treat 404 user_not_found on authenticated endpoints as an invalid session.
- Investigate concurrent deletion or DB read-replica lag if it spikes.
- Clear stale tokens after account deletion on the client.
When it happens
Trigger: An authenticated POST /send-confirm-email whose actor.user.id has no matching row — e.g. the account was deleted between session creation and this call, a DB replication lag gap, or a token minted against a since-purged user.
Common situations: A user triggers deletion then a still-open tab fires the confirm-email call; a test/staging environment where the user row was wiped but sessions persisted; a multi-DB setup where the read replica lags behind a delete.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/41ba29e0dab10f74.
Report an issue: GitHub.