wg-easy/wg-easy · error · Error
User not found
Error message
User not found
What it means
Thrown by UserService.updatePassword() inside a transaction when re-fetching the target user by id returns null. The re-read exists specifically to avoid changing a password for a user deleted while the request was in flight. It means the user id supplied does not resolve to an existing row at update time.
Source
Thrown at src/server/database/repositories/user/service.ts:160
async update(id: ID, name: string, email: string | null) {
return this.#statements.update.execute({ id, name, email });
}
async updatePassword(
id: ID,
currentPassword: string | null,
newPassword: string
) {
const hash = await hashPassword(newPassword);
return this.#db.transaction(async (tx) => {
// get user again to avoid password changing while request
const txUser = await tx.query.user
.findFirst({ where: eq(user.id, id) })
.execute();
if (!txUser) {
throw new Error('User not found');
}
// only check password if already set
if (txUser.password !== null) {
if (!currentPassword) {
throw new Error('Invalid password');
}
const passwordValid = await isPasswordValid(
currentPassword,
txUser.password
);
if (!passwordValid) {
throw new Error('Invalid password');
}
}
View on GitHub (pinned to 5c38c1427a)
Solutions
- Verify the user id exists before/while calling updatePassword and return 404 on failure
- Catch the error and surface 'account no longer exists' instead of a generic failure
- Re-fetch the user in the UI before submitting a password change
- Wrap in try-catch to map to HTTP 404
Example fix
// before
await userService.updatePassword(id, current, next);
// after
try { await userService.updatePassword(id, current, next); }
catch (e) { if (e.message === 'User not found') throw new NotFoundError('user'); throw e; } Defensive patterns
Strategy: retry
Try / catch
for (let i = 0; i < 3; i++) {
try { await userService.updatePassword(id, current, next); break; }
catch (e) { if (e.message === 'User not found') { if (i === 2) throw new NotFoundError('User'); continue; } throw e; }
} Prevention
- Re-fetch the user before mutating account state
- Handle concurrent admin deletions gracefully
- Return 404 semantics to clients so they refresh state
When it happens
Trigger: Calling updatePassword(id, ...) with an id that is not in the user table; the user being deleted between request validation and the transactional update; stale id from a client after the account was removed.
Common situations: Two admin sessions where one deletes the account while the other changes its password; password-reset links used after account deletion; client caching an outdated user list.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
AI-assisted analysis of wg-easy/wg-easy@5c38c1427a (2026-08-30).
Data as JSON: /api/errors/1e9abc69471f34dd.
Report an issue: GitHub.