Mintplex-Labs/anything-llm · warning · Error

Passwords do not match

Error message

Passwords do not match

What it means

resetPassword() compares the trimmed new password against the raw confirmPassword (String(confirmPassword), no trim) and throws on mismatch. Note the asymmetry: 'pass ' passes when confirm is 'pass', but a leading/trailing space in the confirm field alone fails the equality check.

Solutions

  1. Re-enter the password and confirmation so they match exactly
  2. Add client-side equality validation before calling resetPassword
  3. Trim the confirm input client-side to match the server's trimming of the new password
  4. Catch the throw and show a 'passwords do not match' form message instead of a generic failure
Defensive patterns

Strategy: validation

Validate before calling

const pw = newPassword.trim();
const confirm = String(confirmPassword ?? '').trim();
if (!pw || pw !== confirm) {
  return res.status(400).json({ message: 'Passwords do not match or are empty.' });
}

Type guard

function passwordsMatch(a, b) {
  return typeof a === 'string' && typeof b === 'string' && a.trim() === b.trim() && a.trim().length > 0;
}

Try / catch

try {
  await resetPassword(token, newPassword, confirmPassword);
} catch (err) {
  if (err.message === 'Passwords do not match') return res.status(400).json({ message: 'Passwords do not match.' });
  throw err;
}

Prevention

When it happens

Trigger: Confirm field differs from the new password — typo, or whitespace padding: newPassword 'secret' with confirmPassword ' secret ' fails because only the new password is trimmed.

Common situations: User retypes with a typo; browser autofill inserting different values; mobile keyboards adding a trailing space to one field; frontend not comparing fields before submit.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18). Data as JSON: /api/errors/6c5f93909e55f52a. Report an issue: GitHub.

Appendix: source

Thrown at server/utils/PasswordRecovery/index.js:75

    );
    if (index === -1) return false;
    unmatchedHashes.splice(index, 1);
    return true;
  });
  if (!validCodes) return { success: false, error: "Invalid recovery codes." };

  const { passwordResetToken, error } = await PasswordResetToken.create(
    user.id
  );
  if (!!error) return { success: false, error };
  return { success: true, resetToken: passwordResetToken.token };
}

async function resetPassword(token, _newPassword = "", confirmPassword = "") {
  const newPassword = String(_newPassword).trim(); // No spaces in passwords
  if (!newPassword) throw new Error("Invalid password.");
  if (newPassword !== String(confirmPassword))
    throw new Error("Passwords do not match");

  const resetToken = await PasswordResetToken.findUnique({
    token: String(token),
  });
  if (!resetToken || resetToken.expiresAt < new Date()) {
    return { success: false, message: "Invalid reset token" };
  }

  // JOI password rules will be enforced inside .update.
  const { error } = await User.update(resetToken.user_id, {
    password: newPassword,
  });

  // seen_recovery_codes is not publicly writable
  // so we have to do direct update here
  await User._update(resetToken.user_id, {
    seen_recovery_codes: false,
  });

View on GitHub (pinned to 3aec848f28)