hcengineering/platform · error
err.message
Error message
err.message
What it means
The final branch of the global error middleware: any thrown error that is NOT an ApiError becomes an HTTP 500 whose body is the raw err.message. This is the catch-all for unexpected exceptions in all pod endpoints.
Source
Thrown at services/gmail/pod-gmail/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(`Gmail 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 server logs / catchError wrapping to capture the full stack, since the response only carries message
- Reproduce with the same request payload and add targeted handling in the failing handler
- Harden handlers to validate inputs before dereferencing (e.g. req.body fields)
- Convert known error classes to ApiError so clients get 400s with codes instead of opaque 500s
Example fix
// before
const { workspace } = decodeToken(token)
// after
let workspace: string
try {
({ workspace } = decodeToken(token))
} catch (e) {
throw new ApiError('INVALID_TOKEN', 'Token could not be decoded')
} Defensive patterns
Strategy: try-catch
Validate before calling
// pre-validate request shape to avoid server-side TypeErrors
if (typeof body !== 'object' || body == null) throw new Error('request body must be an object') Type guard
function isServerError(body: unknown): body is { message: string } {
return typeof body === 'object' && body !== null && typeof (body as any).message === 'string'
} Try / catch
try {
const res = await fetch(url, opts)
if (res.status >= 500) {
const body = await res.json().catch(() => ({}))
// server bug or transient outage — retry with backoff, then alert
await retryWithBackoff(() => fetch(url, opts))
}
} catch (e) { /* escalate after retries exhausted */ } Prevention
- Treat 500s as retryable with backoff and a retry cap
- Report the raw message plus a request id when filing bugs against the pod
- Favor throwing ApiError from handlers so callers get 400 + code instead of opaque 500s
When it happens
Trigger: Any handler throws a plain Error/TypeError/unknown exception — null dereferences, DB driver errors, JSON parse failures, third-party API exceptions — reaching the Express error middleware.
Common situations: Unvalidated req fields causing TypeErrors, storage layer outages, dependency service errors, bugs introduced after refactors surfacing as opaque 500s.
Related errors
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/f57ac0f875310e45.
Report an issue: GitHub.