moeru-ai/airi · error · Error

requestPasswordReset failed

Error message

requestPasswordReset failed

What it means

Wraps authClient.requestPasswordReset({ email, redirectTo }); the literal fires only when better-auth's error carries no message. The endpoint also covers initial credential set (it creates a credential row when none exists), and redirectTo must be the hosted /auth/reset-password page because a file:// URL would break the emailed link.

Source

Thrown at packages/stage-pages/src/pages/settings/account/account-settings-page.vue:319

  try {
    // NOTICE:
    // `redirectTo` MUST live on the API server origin, not on the current
    // browser origin. This page is shared with apps/stage-tamagotchi,
    // whose Electron renderer loads from `file://` — `window.location.origin`
    // would put a `file://` URL into the reset email and break the flow
    // for any user who clicks from their inbox. The auth UI is hosted at
    // `${SERVER_URL}/auth/reset-password` and reachable from the public
    // internet.
    // Source: PR #1753 review (chatgpt-codex-connector P1).
    //
    // The reset-password endpoint also covers the initial-set case — it
    // creates a credential row when none exists, see
    // node_modules/better-auth/dist/api/routes/password.mjs L152-158.
    const redirectTo = new URL('/auth/reset-password', SERVER_URL).toString()
    const { error } = await authClient.requestPasswordReset({ email, redirectTo })
    if (error)
      throw new Error(error.message ?? 'requestPasswordReset failed')
    setPasswordSuccess.value = t('settings.pages.account.security.message.setLinkSent', { email })
    trackPasswordResetRequested()
  }
  catch (error) {
    setPasswordError.value = errorMessageFrom(error) ?? t('settings.pages.account.security.error.setLinkFailed')
  }
  finally {
    setPasswordLoading.value = false
  }
}

// ---- Delete account ----
//
// Two-step flow:
// (1) Click "Delete account" -> open a reka-ui Dialog with a focus-trap and
//     overlay so the destructive action is unambiguously modal. Inside the
//     dialog the user retypes their email; we only enable the submit button
//     when the entered value matches `userEmail` exactly. This is the same

View on GitHub (pinned to 677329427f)

Solutions

  1. Check the auth server's SMTP/email env configuration — most reset failures are send-side.
  2. Log error.status/code; 429 means wait before retrying.
  3. Confirm SERVER_URL is the public auth origin so redirectTo is valid.
  4. Validate the email format client-side before sending.
Defensive patterns

Strategy: try-catch

Validate before calling

if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
  setPasswordError.value = 'Enter a valid email address'
  return
}

Try / catch

catch (error) {
  if ((error as { status?: number })?.status === 429)
    setPasswordError.value = 'Too many requests — try again later'
  else
    setPasswordError.value = errorMessageFrom(error) ?? t('settings.pages.account.security.error.setLinkFailed')
}

Prevention

When it happens

Trigger: Auth server email/SMTP sender misconfigured so the reset mail fails; rate limiting after repeated requests; invalid email format; SERVER_URL wrong so redirectTo is invalid.

Common situations: Dev auth server without SMTP env vars; users requesting multiple resets in a row; running the UI from file:// where the redirect URL cannot work.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/b4d5ffa6ad1739a8. Report an issue: GitHub.