{"record":{"id":"c54d5038d2ba0e86","repo":"langfuse/langfuse","slug":"email-s-failed-join-could-not-be-sent","errorCode":null,"errorMessage":"Email(s) (${failed.join(\", \")}) could not be sent","messagePattern":"Email\\(s\\) \\((.+?)\\) could not be sent","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/shared/src/server/services/email/passwordReset/sendResetPasswordVerificationRequest.tsx","lineNumber":117,"sourceCode":"\n  const textBody = isSetupMode\n    ? `Welcome to Langfuse! Use the following code to verify your email: ${token}\\n\\nThis code will expire in 3 minutes. If you did not request this, you can ignore this email.`\n    : `Use the following code to reset your Langfuse password: ${token}\\n\\nThis code will expire in 3 minutes. If you did not request a reset, you can ignore this email.`;\n\n  const result = await transport.sendMail({\n    to: identifier,\n    from: provider.from,\n    subject,\n    text: textBody,\n    html: htmlTemplate,\n  });\n  // nodemailer's SES transport omits `rejected`/`pending` from SentMessageInfo,\n  // so guard against undefined before reading them.\n  const failed = [...(result.rejected ?? []), ...(result.pending ?? [])].filter(\n    Boolean,\n  );\n  if (failed.length) {\n    throw new Error(`Email(s) (${failed.join(\", \")}) could not be sent`);\n  }\n}\n\nexport default ResetPasswordTemplate;\n","sourceCodeStart":99,"sourceCodeEnd":122,"githubUrl":"https://github.com/langfuse/langfuse/blob/59d92c7cf365150d10b753b5a0d1708902a2ed60/packages/shared/src/server/services/email/passwordReset/sendResetPasswordVerificationRequest.tsx#L99-L122","documentation":"After attempting to send a password-reset email, the sender checks nodemailer's result for rejected or pending recipients; if any recipient failed, it throws listing the failed addresses. The code notes the SES transport omits these fields, so it guards against undefined before reading them — any non-empty rejection or pending list fails the request.","triggerScenarios":"Calling sendResetPasswordVerificationRequest where the SMTP/transport server rejects the recipient address (e.g. 550 unknown user, invalid domain) or leaves it pending; result.rejected or result.pending then contains the email address.","commonSituations":"User signs up with a typo'd or nonexistent email then requests a password reset; SMTP relay greylists/defers the message (pending); mailbox full or domain MX missing; transport misconfiguration causing partial failures.","solutions":["Verify the recipient email address is valid and deliverable before sending (format + MX check)","Check SMTP/SES provider logs and dashboards for the rejection reason (bounce, greylist, policy)","If greylisting/pending is transient, retry after a delay or let the user resend","Confirm transport configuration (host, port, auth) points at the intended relay"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const emailSchema = z.string().email();\nif (!emailSchema.safeParse(user.email).success) {\n  return { ok: false, reason: \"invalid-email\" };\n}","typeGuard":"const hasRejectedRecipients = (\n  info: SentMessageInfo\n): info is SentMessageInfo & { rejected: string[] } =>\n  Array.isArray(info.rejected) && info.rejected.length > 0;","tryCatchPattern":"try {\n  await sendResetPasswordVerificationRequest({ email });\n} catch (err) {\n  if (err instanceof Error && /could not be sent/.test(err.message)) {\n    res.status(422).json({ error: \"We could not deliver a reset email to that address. Verify it and try again.\" });\n    return;\n  }\n  throw err;\n}","preventionTips":["Validate email syntax and MX records before enqueueing sends","Monitor bounces/complaints via SES SNS feedback and suppress repeat-failing addresses","Use a dedicated retry queue for transient SMTP failures (greylisting) separate from hard rejections"],"tags":["email","smtp","nodemailer","password-reset"],"backgroundTag":"email-delivery-rejected","analyzedSha":"59d92c7cf365150d10b753b5a0d1708902a2ed60","analyzedAt":"2026-08-27T22:22:00.402Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}