actualbudget/actual · error
User not found
Error message
User not found
What it means
HTTP 400 from GET /validate with `reason:'User not found'`. The session token was cryptographically valid, but `getUserInfo(session.user_id)` returned no row — i.e. the user behind the token was deleted from the account database after the token was issued. The endpoint refuses to validate a session whose backing user no longer exists.
Source
Thrown at packages/sync-server/src/app-account.js:193
const { prefs } = req.body || {};
if (!prefs || typeof prefs !== 'object') {
res.status(400).send({ status: 'error', reason: 'invalid-prefs' });
return;
}
setServerPrefs(prefs);
res.send({ status: 'ok', data: {} });
});
app.get('/validate', (req, res) => {
const session = validateSession(req, res);
if (session) {
const user = getUserInfo(session.user_id);
if (!user) {
res.status(400).send({ status: 'error', reason: 'User not found' });
return;
}
res.send({
status: 'ok',
data: {
validated: true,
userName: user?.user_name,
permission: user?.role,
userId: session?.user_id,
displayName: user?.display_name,
loginMethod: session?.auth_method,
prefs: getServerPrefs(),
},
});
}
});
View on GitHub (pinned to d4334cb6e6)
Solutions
- Log in again to obtain a fresh token for an existing user.
- Check the users list (admin GET /users) to confirm the user still exists; re-create the user if it was deleted unintentionally.
- Clear the client's cached token when users are removed server-side so it doesn't keep validating a dead session.
Example fix
// before: blindly reusing a stored token
const res = await api.get('/validate', { headers: { 'X-ACTUAL-TOKEN': storedToken } });
// after: re-login when validation reports a missing user
if (res.data?.reason === 'User not found') {
const { token } = await login(username, password);
storedToken = token;
} Defensive patterns
Strategy: fallback
Validate before calling
// a token can only be validated server-side, but you can detect the stale-user case from the response if (validateRes.data?.reason === 'User not found') invalidateStoredToken();
Type guard
function isUserMissing(res) {
return res?.status === 'error' && res.reason === 'User not found';
} Try / catch
try {
const res = await get('/validate', { headers: authHeaders(token) });
if (res.data.data.validated) return res.data.data;
throw new SessionError(res.data.reason);
} catch (e) {
if (isUserMissing(e.response?.data)) {
clearToken();
return startLoginFlow(); // fallback: re-login
}
throw e;
} Prevention
- Clear cached tokens whenever the admin deletes or recreates users
- Treat 'User not found' on /validate as an automatic re-login trigger
- Avoid restoring account.sqlite backups without notifying connected clients
When it happens
Trigger: GET /validate with a token whose user_id was deleted (DELETE /users or direct DB removal) between login and validation; stale tokens persisted by a client across a server user wipe.
Common situations: Admin deletes users while their clients are still running; restoring an old account.sqlite backup that lacks recently created users; DB re-creation during server re-bootstrap while clients keep cached tokens.
Related errors
- invalid-password
- No token received.
- token-expired
- responseData.description || responseData.reason || 'unknown'
- unknown
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/735f5f346511ddeb.
Report an issue: GitHub.