{"record":{"id":"02598f7c82e7d84a","repo":"koala73/worldmonitor","slug":"invalid-email-address","errorCode":null,"errorMessage":"Invalid email address","messagePattern":"Invalid email address","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"server/worldmonitor/leads/v1/register-interest.ts","lineNumber":325,"sourceCode":"      throw new ApiError(503, 'Rate-limit service temporarily unavailable', '');\n    }\n    if (!scoped.allowed) {\n      throw new ApiError(429, 'Too many requests', '');\n    }\n  } else {\n    const turnstileOk = await verifyTurnstile({\n      token: req.turnstileToken || '',\n      ip,\n      logPrefix: '[register-interest]',\n    });\n    if (!turnstileOk) {\n      throw new ApiError(403, 'Bot verification failed', '');\n    }\n  }\n\n  const { email, source, appVersion, referredBy } = req;\n  if (!email || email.length > MAX_EMAIL_LENGTH || !EMAIL_RE.test(email)) {\n    throw new ValidationError([{ field: 'email', description: 'Invalid email address' }]);\n  }\n\n  const emailCheck = await validateEmail(email);\n  if (!emailCheck.valid) {\n    throw new ValidationError([{ field: 'email', description: emailCheck.reason }]);\n  }\n\n  const safeSource = source ? source.slice(0, MAX_META_LENGTH) : 'unknown';\n  const safeAppVersion = appVersion ? appVersion.slice(0, MAX_META_LENGTH) : 'unknown';\n  const safeReferredBy = referredBy ? referredBy.slice(0, 20) : undefined;\n\n  const convexUrl = process.env.CONVEX_URL;\n  if (!convexUrl) {\n    throw new ApiError(503, 'Registration service unavailable', '');\n  }\n\n  const client = new ConvexHttpClient(convexUrl);\n  const result = (await client.mutation(api.registerInterest.register, {","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/koala73/worldmonitor/blob/a96956387a927b8cd7aa34b0c41fca357e746be9/server/worldmonitor/leads/v1/register-interest.ts#L307-L343","documentation":"Syntactic gate on the waitlist registration (register-interest.ts:324): the email must be truthy, at most MAX_EMAIL_LENGTH=320 chars, and match /^[^\\s@]+@[^^\\s@]+\\.[^s@]+$/. It rejects anything without exactly one @, a dot in the domain part, or containing whitespace. This runs BEFORE the deeper validateEmail() content checks (error 311).","triggerScenarios":"Submitting \"user@localhost\" (no dot in domain), \"user @mail.com\" (space), \"user@@mail.com\", a >320-char address, undefined/null email, or untrimmed input with trailing spaces.","commonSituations":"Free-text input without client validation; pasting emails that carry trailing whitespace or a semicolon from an address book; test payloads with placeholder strings like \"test\" or \"a@b\".","solutions":["Trim input and run the same regex client-side before submit","Use <input type=\"email\"> plus a required check in the form","Enforce the 320-char cap on the input's maxlength","Only after this passes, expect deeper domain checks — fix syntax first"],"exampleFix":"// before\nawait registerInterest({ email: '  jane@doe' , ... }); // no dot + spaces -> ValidationError\n\n// after\nconst email = rawEmail.trim();\nif (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email) || email.length > 320) throw new Error('fix email');\nawait registerInterest({ email, ... });","handlingStrategy":"validation","validationCode":"const EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nconst email = rawInput.trim();\nif (!email || email.length > 320 || !EMAIL_RE.test(email)) {\n  showFieldError('email', 'Enter a valid email address');\n  return;\n}\nawait registerInterest({ ...req, email });","typeGuard":"function isSyntacticEmail(v: unknown): v is string {\n  return typeof v === 'string' && v.length <= 320 && /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v);\n}","tryCatchPattern":"try { await registerInterest(req); }\ncatch (e) {\n  if (isValidationError(e) && e.violations?.some(v => v.field === 'email')) {\n    showFieldError('email', e.violations.find(v => v.field === 'email')!.description);\n    return;\n  }\n  throw e;\n}","preventionTips":["Trim email input and enforce maxlength=320 on the field","Run the identical regex client-side before spending a Turnstile token","Use type=\"email\" + required so the browser blocks the obvious cases"],"tags":["email","validation","form-input"],"backgroundTag":"invalid-email-format","analyzedSha":"a96956387a927b8cd7aa34b0c41fca357e746be9","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}