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

  1. Verify the user id exists before/while calling updatePassword and return 404 on failure
  2. Catch the error and surface 'account no longer exists' instead of a generic failure
  3. Re-fetch the user in the UI before submitting a password change
  4. 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

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.