{"record":{"id":"b308656c70ab457f","repo":"hcengineering/platform","slug":"err-message-b30865","errorCode":null,"errorMessage":"err.message","messagePattern":"err\\.message","errorType":"http","errorClass":null,"httpStatus":500,"severity":"critical","filePath":"services/mail/pod-mail/src/server.ts","lineNumber":57,"sourceCode":"  endpoints.forEach((endpoint) => {\n    if (endpoint.type === 'get') {\n      app.get(endpoint.endpoint, catchError(endpoint.handler))\n    } else if (endpoint.type === 'post') {\n      app.post(endpoint.endpoint, catchError(endpoint.handler))\n    }\n  })\n\n  app.use((_req, res, _next) => {\n    res.status(404).send({ message: 'Not found' })\n  })\n\n  app.use((err: any, _req: any, res: any, _next: any) => {\n    if (err instanceof ApiError) {\n      res.status(400).send({ code: err.code, message: err.message })\n      return\n    }\n\n    res.status(500).send({ message: err.message })\n  })\n\n  return app\n}\n\nexport function listen (e: Express, port: number, host?: string): Server {\n  const cb = (): void => {\n    console.log(`Mail service has been started at ${host ?? '*'}:${port}`)\n  }\n\n  return host !== undefined ? e.listen(port, host, cb) : e.listen(port, cb)\n}\n","sourceCodeStart":39,"sourceCodeEnd":70,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/mail/pod-mail/src/server.ts#L39-L70","documentation":"The pod-mail error-handling middleware's final fallback responds HTTP 500 with { message: err.message } for any thrown error that is NOT an ApiError. This is an unexpected/unhandled exception inside an endpoint handler (bug, network failure to SMTP, unhandled null, etc.) surfaced as a 500. The message is the raw error message, which may be opaque or expose internals.","triggerScenarios":"A handler throws a non-ApiError (TypeError, nodemailer SMTP failure, JSON parse error, network error to an upstream service) and catchError forwards it to the error middleware, which fails the `err instanceof ApiError` check.","commonSituations":"SMTP host unreachable or credentials wrong causing nodemailer to throw; null/undefined dereference in handler logic; transient network failures; dependency versions changed so an internal call signature broke.","solutions":["Check the mail pod's server logs at the time of the 500 — the response only carries err.message, the stack is on the server.","Fix the underlying exception in the handler (validate inputs, null-check, wrap SMTP calls with proper error handling).","Convert expected failure modes into ApiError throws so clients receive 400 with a meaningful code instead of a 500.","Add retry-with-backoff in the client for transient SMTP/network failures before re-raising."],"exampleFix":"// before\nasync function handler(req, res) {\n  const info = await transporter.sendMail(opts) // throws raw SMTP errors\n}\n// after\nasync function handler(req, res) {\n  try {\n    const info = await transporter.sendMail(opts)\n  } catch (e) {\n    throw new ApiError('MAIL_DELIVERY_FAILED', (e as Error).message)\n  }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const res = await fetch(mailUrl, opts)\n  if (res.status === 500) {\n    const { message } = await res.json()\n    throw new Error(`mail pod crashed: ${message} (check pod logs for stack)`)\n  }\n} catch (e) { /* alert/retry with backoff for transient SMTP/network failures */ }","preventionTips":["Retry with exponential backoff only for transient-looking 500s (SMTP/network)","Alert on 500 rates rather than single occurrences","Convert expected failure modes server-side into ApiErrors so they stop showing up as 500s"],"tags":["http-500","unhandled-exception","express","server-error"],"backgroundTag":"unhandled-exception-500","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}