{"record":{"id":"260b66714ec73d31","repo":"Budibase/budibase","slug":"user-must-be-provided-for-password-recovery","errorCode":null,"errorMessage":"User must be provided for password recovery.","messagePattern":"User must be provided for password recovery\\.","errorType":"http","errorClass":"HTTPError","httpStatus":400,"severity":"error","filePath":"packages/worker/src/utilities/email.ts","lineNumber":154,"sourceCode":" * @return returns details about the attempt to send email, e.g. if it is successful; based on\n * nodemailer response.\n */\nexport async function sendEmail(\n  email: string,\n  purpose: EmailTemplatePurpose,\n  opts: SendEmailOpts\n) {\n  const config = await configs.getSMTPConfig(opts?.automation)\n  if (!config && !TEST_MODE) {\n    throw \"Unable to find SMTP configuration.\"\n  }\n  const transport = createSMTPTransport(config)\n  // if there is a link code needed this will retrieve it\n  let code: string | null = null\n  switch (purpose) {\n    case EmailTemplatePurpose.PASSWORD_RECOVERY:\n      if (!opts.user || !opts.user._id) {\n        throw new HTTPError(\"User must be provided for password recovery.\", 400)\n      }\n      code = await cache.passwordReset.createCode(opts.user._id, opts.info)\n      break\n    case EmailTemplatePurpose.INVITATION:\n      code = await cache.invite.createCode(email, opts.info)\n      break\n  }\n  let context = await getSettingsTemplateContext(purpose, code)\n\n  let message: Parameters<typeof transport.sendMail>[0] = {\n    from: opts?.from || config?.from,\n    html: await buildEmail(purpose, email, context, {\n      user: opts?.user,\n      contents: opts?.contents,\n    }),\n  }\n  if (opts?.attachments) {\n    let attachments = await Promise.all(","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/worker/src/utilities/email.ts#L136-L172","documentation":"sendEmail requires a user object with an _id when the purpose is PASSWORD_RECOVERY, because it must generate a password-reset code bound to that user's id. If opts.user or opts.user._id is missing it throws HTTPError 400 \"User must be provided for password recovery.\"","triggerScenarios":"Calling sendEmail with purpose EmailTemplatePurpose.PASSWORD_RECOVERY without passing opts.user, or passing a user object lacking an _id field.","commonSituations":"Custom code that looks up the user by email but the lookup returned undefined/empty; constructing email options manually and forgetting the user field; passing a user record from a different DB where _id wasn't projected.","solutions":["Fetch the user first (e.g. getGlobalUserByEmail) and pass the full record, including _id, as opts.user","Validate the user object exists and has _id before invoking sendEmail","Confirm you are using the right purpose constant — INVITATION does not need user._id but PASSWORD_RECOVERY does","If the user lookup failed, return a safe 'no such user' response instead of proceeding to send"],"exampleFix":"// before\nawait sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, { info })\n// after\nconst user = await userSdk.core.getGlobalUserByEmail(email)\nif (!user || !user._id) return\nawait sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, { user, info })","handlingStrategy":"validation","validationCode":"if (!user || !user._id) {\n  throw new Error(\"Cannot send password recovery without a resolved user\")\n}","typeGuard":"function hasId(user: Partial<User> | undefined): user is User & { _id: string } {\n  return !!user && typeof user._id === \"string\" && user._id.length > 0\n}","tryCatchPattern":"try {\n  await sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, { user, info })\n} catch (e) {\n  if (e.message.includes(\"User must be provided\")) {\n    // user lookup failed — handle as unknown-email case\n  }\n}","preventionTips":["Resolve the user record (with _id) before calling sendEmail for PASSWORD_RECOVERY","Validate user objects loaded from external sources include _id","Use the correct EmailTemplatePurpose for each email type"],"tags":["email","validation","password-reset","api"],"backgroundTag":"missing-required-parameter","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}