Budibase/budibase · error · HTTPError

User must be provided for password recovery.

Error message

User must be provided for password recovery.

What it means

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."

Source

Thrown at packages/worker/src/utilities/email.ts:154

 * @return returns details about the attempt to send email, e.g. if it is successful; based on
 * nodemailer response.
 */
export async function sendEmail(
  email: string,
  purpose: EmailTemplatePurpose,
  opts: SendEmailOpts
) {
  const config = await configs.getSMTPConfig(opts?.automation)
  if (!config && !TEST_MODE) {
    throw "Unable to find SMTP configuration."
  }
  const transport = createSMTPTransport(config)
  // if there is a link code needed this will retrieve it
  let code: string | null = null
  switch (purpose) {
    case EmailTemplatePurpose.PASSWORD_RECOVERY:
      if (!opts.user || !opts.user._id) {
        throw new HTTPError("User must be provided for password recovery.", 400)
      }
      code = await cache.passwordReset.createCode(opts.user._id, opts.info)
      break
    case EmailTemplatePurpose.INVITATION:
      code = await cache.invite.createCode(email, opts.info)
      break
  }
  let context = await getSettingsTemplateContext(purpose, code)

  let message: Parameters<typeof transport.sendMail>[0] = {
    from: opts?.from || config?.from,
    html: await buildEmail(purpose, email, context, {
      user: opts?.user,
      contents: opts?.contents,
    }),
  }
  if (opts?.attachments) {
    let attachments = await Promise.all(

View on GitHub (pinned to a81a902e9a)

Solutions

  1. Fetch the user first (e.g. getGlobalUserByEmail) and pass the full record, including _id, as opts.user
  2. Validate the user object exists and has _id before invoking sendEmail
  3. Confirm you are using the right purpose constant — INVITATION does not need user._id but PASSWORD_RECOVERY does
  4. If the user lookup failed, return a safe 'no such user' response instead of proceeding to send

Example fix

// before
await sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, { info })
// after
const user = await userSdk.core.getGlobalUserByEmail(email)
if (!user || !user._id) return
await sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, { user, info })
Defensive patterns

Strategy: validation

Validate before calling

if (!user || !user._id) {
  throw new Error("Cannot send password recovery without a resolved user")
}

Type guard

function hasId(user: Partial<User> | undefined): user is User & { _id: string } {
  return !!user && typeof user._id === "string" && user._id.length > 0
}

Try / catch

try {
  await sendEmail(email, EmailTemplatePurpose.PASSWORD_RECOVERY, { user, info })
} catch (e) {
  if (e.message.includes("User must be provided")) {
    // user lookup failed — handle as unknown-email case
  }
}

Prevention

When it happens

Trigger: Calling sendEmail with purpose EmailTemplatePurpose.PASSWORD_RECOVERY without passing opts.user, or passing a user object lacking an _id field.

Common situations: 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.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/260b66714ec73d31. Report an issue: GitHub.