{"record":{"id":"16bf36db0c4a6d27","repo":"nextauthjs/next-auth","slug":"missing-email-from-request-body","errorCode":null,"errorMessage":"Missing email from request body.","messagePattern":"Missing email from request body\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/lib/actions/signin/send-token.ts","lineNumber":95,"sourceCode":"\n  const createToken = adapter!.createVerificationToken?.({\n    identifier: email,\n    token: await createHash(`${token}${secret}`),\n    expires,\n  })\n\n  await Promise.all([sendRequest, createToken])\n\n  return {\n    redirect: `${baseUrl}/verify-request?${new URLSearchParams({\n      provider: provider.id,\n      type: provider.type,\n    })}`,\n  }\n}\n\nexport function defaultNormalizer(email?: string) {\n  if (!email) throw new Error(\"Missing email from request body.\")\n\n  // Apply Unicode NFKC normalization *before* validation. Without this, a\n  // character that is a homoglyph of `@` (e.g. U+FF20 FULLWIDTH COMMERCIAL AT)\n  // passes the single-`@` check below, but can later be canonicalized to an\n  // ASCII `@` by a downstream address parser, splitting the address into\n  // multiple recipients. Normalizing first ensures any such homoglyph is\n  // turned into a real `@` and rejected by the checks below.\n  const trimmedEmail = email.normalize(\"NFKC\").toLowerCase().trim()\n\n  // Reject email addresses with quotes to prevent address parser confusion\n  // This prevents attacks like \"attacker@evil.com\"@victim.com\n  if (trimmedEmail.includes('\"')) {\n    throw new Error(\"Invalid email address format.\")\n  }\n\n  // Get the first two elements only,\n  // separated by `@` from user input.\n  let [local, domain] = trimmedEmail.split(\"@\")","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/nextauthjs/next-auth/blob/a1a16a5a7780488c7449feece410033f445d0b31/packages/core/src/lib/actions/signin/send-token.ts#L77-L113","documentation":"Before sending a sign-in email, Auth.js normalizes the email from the request body via defaultNormalizer. If the body contains no email (undefined or empty string), this plain Error is thrown because there is no address to send a verification token to.","triggerScenarios":"POSTing to /api/auth/signin/email (or provider id) with a body missing the email field, an empty email, or a field named differently than the library expects (the form key must be 'email'); JSON bodies that fail to parse into { email }.","commonSituations":"Custom sign-in form submitting without the email input filled or with a misspelled input name; sending JSON instead of form-urlencoded where the server expects urlencoded body; server actions or fetch calls that forget to append email to FormData; email-only input validation missing on the client.","solutions":["Ensure the POST body includes an 'email' field: use FormData or URLSearchParams with key 'email' and a non-empty value","Add client-side required/email-format validation before submitting the sign-in form","If posting JSON, confirm your route forwards it correctly — Auth.js expects form-encoded bodies by default","Verify the input element's name attribute is exactly 'email'"],"exampleFix":"// before\nawait fetch('/api/auth/signin/email', { method: 'POST', body: JSON.stringify({ mail: email }) });\n// after\nconst body = new URLSearchParams({ email, csrfToken });\nawait fetch('/api/auth/signin/email', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n  credentials: 'include',\n  body,\n});","handlingStrategy":"validation","validationCode":"function canSubmitEmail(v: unknown): v is string {\n  return typeof v === 'string' && v.trim().length > 0 && v.includes('@');\n}\nif (!canSubmitEmail(formData.get('email'))) throw new Error('Email is required');","typeGuard":"function isNonEmptyEmail(v: unknown): v is string {\n  return typeof v === 'string' && v.trim() !== '';\n}","tryCatchPattern":"try {\n  await signIn('email', { email });\n} catch (e) {\n  if (/Missing email from request body/.test(String(e))) {\n    // re-render the form with a 'email is required' message\n  }\n}","preventionTips":["Mark the email input required and validate before submit","Use the exact body key 'email' with form-urlencoded or FormData encoding","Never send a template placeholder like '${email}' unrendered","Add an integration test that posts a real email to /api/auth/signin/email"],"tags":["email","validation","http","input"],"backgroundTag":"missing-request-field","analyzedSha":"a1a16a5a7780488c7449feece410033f445d0b31","analyzedAt":"2026-08-28T21:52:38.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}