hcengineering/platform · critical
err.message
Error message
err.message
What it means
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.
Source
Thrown at services/mail/pod-mail/src/server.ts:57
endpoints.forEach((endpoint) => {
if (endpoint.type === 'get') {
app.get(endpoint.endpoint, catchError(endpoint.handler))
} else if (endpoint.type === 'post') {
app.post(endpoint.endpoint, catchError(endpoint.handler))
}
})
app.use((_req, res, _next) => {
res.status(404).send({ message: 'Not found' })
})
app.use((err: any, _req: any, res: any, _next: any) => {
if (err instanceof ApiError) {
res.status(400).send({ code: err.code, message: err.message })
return
}
res.status(500).send({ message: err.message })
})
return app
}
export function listen (e: Express, port: number, host?: string): Server {
const cb = (): void => {
console.log(`Mail service has been started at ${host ?? '*'}:${port}`)
}
return host !== undefined ? e.listen(port, host, cb) : e.listen(port, cb)
}
View on GitHub (pinned to 63e28dc964)
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.
Example fix
// before
async function handler(req, res) {
const info = await transporter.sendMail(opts) // throws raw SMTP errors
}
// after
async function handler(req, res) {
try {
const info = await transporter.sendMail(opts)
} catch (e) {
throw new ApiError('MAIL_DELIVERY_FAILED', (e as Error).message)
}
} Defensive patterns
Strategy: try-catch
Try / catch
try {
const res = await fetch(mailUrl, opts)
if (res.status === 500) {
const { message } = await res.json()
throw new Error(`mail pod crashed: ${message} (check pod logs for stack)`)
}
} catch (e) { /* alert/retry with backoff for transient SMTP/network failures */ } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/b308656c70ab457f.
Report an issue: GitHub.