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
- 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
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
- 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
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
- No user ID provided for getting
- Email trigger inputs are required
- Replacing members is not allowed
- Unable to delete self.
- Please contact your platform administrator, SMTP is not conf
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/260b66714ec73d31.
Report an issue: GitHub.