hcengineering/platform · error

err.message

Error message

err.message

What it means

The pod-mail-worker's Express error middleware returns 500 with { message: err.message } for any unhandled route error. For /mta-hook specifically it instead responds 200 { action: 'accept' } so the upstream mail server keeps accepting emails. Seeing this 500 means an error escaped a non-mta-hook route (or an error middleware path other than the mta-hook branch).

Source

Thrown at services/mail/pod-mail-worker/src/index.ts:87

      }
    })()
  }

  app.post('/mta-hook', catchError(handleMtaHook))

  app.use((_req, res, _next) => {
    res.status(404).send({ message: 'Not found' })
  })

  app.use((err: any, req: Request, res: any, _next: any) => {
    ctx.error(err)
    if (req.path === '/mta-hook') {
      // Any error in the mta-hook should not prevent the mail server from handling emails
      // At least the PayloadTooLargeError falls here before reacing our code
      res.status(200).send({ action: 'accept' })
      return
    }
    res.status(500).send({ message: err.message })
  })

  const server = app.listen(config.port, () => {
    ctx.info('server started', {
      ...config,
      secret: config.secret !== undefined ? '(stripped)' : undefined,
      hookToken: config.hookToken !== undefined ? '(stripped)' : undefined,
      storageConfig: config.storageConfig !== undefined ? '(stripped)' : undefined
    })
  })
  await MailWorker.create(ctx)

  const shutdown = (): void => {
    try {
      MailWorker.getMailWorker()
        .close()
        .catch((err) => {
          ctx.error('Failed to close MailWorker', { error: err.message })

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Read the returned err.message (and worker logs via ctx.error) to identify the underlying failure.
  2. If it is PayloadTooLargeError, reduce the payload size or raise the body-parser limit configured in the worker.
  3. If it is a JSON parse error, send valid JSON with Content-Type: application/json.
  4. Fix or wrap the failing route handler with catchError so errors are handled where they occur.

Example fix

// before
app.post('/hook', async (req, res) => { throw new Error('boom') }) // becomes 500 err.message
// after
app.post('/hook', catchError(async (req, res) => { ... }))
Defensive patterns

Strategy: try-catch

Validate before calling

// Guard payload size and JSON validity before sending
const serialized = JSON.stringify(payload)
if (serialized.length > MAX_BODY_BYTES) throw new Error('payload exceeds body-parser limit')
JSON.parse(serialized) // throws early on non-serializable/corrupt payload

Type guard

function isSerializableJson(v: unknown): boolean {
  try { JSON.stringify(v); return true } catch { return false }
}

Try / catch

try {
  const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) })
  if (!res.ok) {
    const { message } = await res.json()
    throw new Error(`worker 500: ${message}`)
  }
} catch (e) {
  ctx.error('mail worker request failed', { e })
}

Prevention

When it happens

Trigger: An uncaught exception thrown synchronously or via next(err) in a pod-mail-worker route — e.g. body-parser PayloadTooLargeError on a non-mta-hook route, JSON parse failure, or a handler crash reaching the error middleware outside the req.path === '/mta-hook' branch.

Common situations: Sending an oversized payload that exceeds the body-parser limit; sending malformed JSON that the body parser rejects; a bug in a newly added worker route; proxies forwarding requests with unexpected content types.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/0b5f889253e73c4c. Report an issue: GitHub.