{"record":{"id":"aea70ef86c7b1b66","repo":"Budibase/budibase","slug":"cannot-reset-password","errorCode":null,"errorMessage":"Cannot reset password.","messagePattern":"Cannot reset password\\.","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/worker/src/api/controllers/global/auth.ts","lineNumber":272,"sourceCode":"  }\n}\n\n/**\n * Perform the user password update if the provided reset code is valid.\n */\nexport const resetUpdate = async (\n  ctx: Ctx<PasswordResetUpdateRequest, PasswordResetUpdateResponse>\n) => {\n  const { resetCode, password } = ctx.request.body\n  try {\n    await authSdk.resetUpdate(resetCode, password)\n    ctx.body = {\n      message: \"password reset successfully.\",\n    }\n  } catch (err: any) {\n    console.warn(err)\n    // hide any details of the error for security\n    ctx.throw(400, err.message || \"Cannot reset password.\")\n  }\n}\n\n// DATASOURCE\n\nexport const datasourcePreAuth = async (\n  ctx: UserCtx<void, void>,\n  next: Next\n) => {\n  const provider = ctx.params.provider\n  const returnPath =\n    typeof ctx.query.returnPath === \"string\" ? ctx.query.returnPath : undefined\n  const { middleware } = require(`@budibase/backend-core`)\n  const handler = middleware.datasource[provider]\n  if (!handler) {\n    ctx.throw(400, \"Unsupported datasource provider\")\n  }\n","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/worker/src/api/controllers/global/auth.ts#L254-L290","documentation":"The worker's resetUpdate endpoint wraps the entire password-reset flow in a try/catch and, for security, re-throws any underlying failure as a 400 with either the original message or the generic fallback 'Cannot reset password.'. The real cause (invalid/expired reset code, user not found, CouchDB error) is intentionally hidden so attackers cannot probe which reset tokens are valid. Whenever you see this message it means an exception escaped the reset handler, not that the new password itself was rejected.","triggerScenarios":"POST to the worker password-reset endpoint (resetUpdate) where: the reset code stored in the invite cache is missing, expired or for a different tenant; cache.invite.updateCode throws; the target user cannot be found; or any other exception occurs inside the handler.","commonSituations":"User clicks an emailed reset link after the code expired or was already consumed; Redis/CouchDB outage while resolving the reset code; multi-tenant setups where the tenantId is not supplied so the code is looked up in the wrong tenant; stale email links pointing at a different environment.","solutions":["Request a fresh password-reset email and use the new link promptly (codes are single-use and expire).","Check worker logs for the console.warn(err) output to see the underlying cause that was hidden from the client.","Verify Redis/invite cache connectivity on the worker; restart it if the cache is unreachable.","For multi-tenant deployments, confirm the request includes the correct tenantId so the code is looked up in the right tenant DB."],"exampleFix":"// before: reusing an expired link fails with 'Cannot reset password.'\nfetch(`/api/global/auth/reset?code=${oldCode}`, { ... })\n// after: fetch a new code, then reset\nconst { code } = await requestReset(email)\nawait resetPassword({ code, password: newPassword })","handlingStrategy":"try-catch","validationCode":"// client-side pre-check before calling reset\nif (!code || !newPassword || newPassword.length < 8) {\n  throw new Error(\"Reset code and a valid new password are required\")\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.resetPassword({ code, password })\n} catch (e) {\n  // 400 hides the real cause for security; treat as 'request a new reset email'\n  notifyUser(\"Reset failed or link expired — please request a new reset email\")\n}","preventionTips":["Use reset links immediately; codes are single-use and expire","Always send the correct tenantId in multi-tenant setups","Monitor worker logs for the underlying console.warn(err) cause"],"tags":["auth","password-reset","worker","security"],"backgroundTag":"password-reset-token-invalid","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}