kriasoft/react-starter-kit · error · Error

Invalid email address: ${email}

Error message

Invalid email address: ${email}

What it means

sendEmail() validates every recipient with the Zod schema z.email() before touching the Resend client. If any address in options.to (string or array) fails safeParse, it throws this plain Error with the offending address interpolated. It is a pre-flight guard so malformed recipients never reach the provider API.

Source

Thrown at apps/api/lib/email.ts:39

type EmailEnv = Pick<Env, "RESEND_API_KEY" | "RESEND_EMAIL_FROM">;

type TemplateEnv = EmailEnv & Pick<Env, "APP_NAME" | "APP_ORIGIN">;

/**
 * Send an email using the Resend client.
 *
 * @param env Environment variables containing Resend configuration
 * @param options Email configuration
 */
export async function sendEmail(env: EmailEnv, options: EmailOptions) {
  const emailSchema = z.email();

  // Validate all recipients before sending
  const recipients = Array.isArray(options.to) ? options.to : [options.to];
  for (const email of recipients) {
    const result = emailSchema.safeParse(email);
    if (!result.success) {
      throw new Error(`Invalid email address: ${email}`);
    }
  }

  if (!env.RESEND_API_KEY) {
    throw new Error("RESEND_API_KEY environment variable is required");
  }

  if (!env.RESEND_EMAIL_FROM) {
    throw new Error("RESEND_EMAIL_FROM environment variable is required");
  }

  // A fresh client per send: the Workers runtime reuses an isolate across
  // requests, and a module-level client would outlive the env it was built from.
  const resend = new Resend(env.RESEND_API_KEY);

  try {
    const result = await resend.emails.send({
      from: options.from || env.RESEND_EMAIL_FROM,

View on GitHub (pinned to 0aa7603435)

Solutions

  1. Log the interpolated address in the message and fix the upstream code or data that produced it
  2. Validate the recipient with z.email().safeParse() (or check it with a regex) before calling any send* function
  3. Treat 'Jane <jane@x.com>' style headers as invalid: strip to the bare address or use Resend's displayName support on the 'from' field, not 'to'
  4. Add a NOT NULL / format constraint on the email column in the database to stop bad values at write time

Example fix

// before
await sendOTP(env, { email: userInput.name + '@', otp, type: 'sign-in' });
// after
const parsed = z.email().safeParse(userInput.email);
if (!parsed.success) return { ok: false, reason: 'invalid-email' };
await sendOTP(env, { email: parsed.data, otp, type: 'sign-in' });
Defensive patterns

Strategy: validation

Validate before calling

import { z } from 'zod';
const emailSchema = z.email();
function assertValidRecipient(to: string | string[]) {
  const list = Array.isArray(to) ? to : [to];
  for (const e of list) {
    const r = emailSchema.safeParse(e.trim());
    if (!r.success) throw new Error(`Refusing to send: invalid recipient '${e}'`);
  }
}

Type guard

function isValidEmail(value: unknown): value is string {
  return typeof value === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}

Try / catch

try {
  await sendVerificationEmail(env, { user, url });
} catch (error) {
  if (error instanceof Error && error.message.startsWith('Invalid email address:')) {
    // surface a field-level validation error to the user, not a 500
    return { ok: false, field: 'email', message: 'Please provide a valid email address' };
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling sendVerificationEmail/sendPasswordReset/sendOTP with a user.email that is empty, missing an @, contains spaces, or any sendEmail call with options.to being '', 'not-an-email', or an array containing such a value.

Common situations: Unverified user-supplied input stored in the database (e.g. legacy rows or OAuth accounts without an email), concatenating form fields incorrectly, or passing a display string like 'Jane <jane@x.com>' instead of a bare address.

Related errors


AI-assisted analysis of kriasoft/react-starter-kit@0aa7603435 (2026-08-31). Data as JSON: /api/errors/7e37d0a8d88d7cb2. Report an issue: GitHub.