hcengineering/platform · error · ApiError

err.code

err.code

Error message

err.message

What it means

The global Express error middleware maps ApiError instances to HTTP 400 with the error's code and message. Any handler that throws/rejects with an ApiError (validation failures, bad requests) lands here.

Source

Thrown at services/gmail/pod-gmail/src/server.ts:53

  app.use(cors())
  app.use(express.json())

  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

  1. Read the returned code field to identify which ApiError subclass fired
  2. Correct the request payload/token that failed validation
  3. Inspect the failing endpoint's controller to see which inputs throw ApiError
  4. Wrap client calls to parse the { code, message } shape for actionable feedback

Example fix

// before
const data = await res.json()
console.log(data)
// after
const data = await res.json()
if (!res.ok && data.code) {
  throw new Error(`Request rejected (${data.code}): ${data.message}`)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// validate inputs that controllers typically reject with ApiError
if (!token || !socialId) throw new Error('token and socialId are required')

Type guard

function isApiErrorResponse(body: unknown): body is { code: string; message: string } {
  return typeof body === 'object' && body !== null && 'code' in body && 'message' in body
}

Try / catch

try {
  const res = await fetch(url, opts)
  const body = await res.json()
  if (res.status === 400 && isApiErrorResponse(body)) {
    throw new ApiClientError(body.code, body.message)
  }
  return body
} catch (e) {
  if (e instanceof ApiClientError) { /* branch on e.code */ }
  throw e
}

Prevention

When it happens

Trigger: Any endpoint handler (e.g. via catchError wrapper) throws an ApiError — invalid input decoded by controllers, malformed tokens, or business-rule rejections.

Common situations: Clients sending payloads that fail controller validation; decodeToken throwing a token-related ApiError; upstream services returning ApiError subclasses that propagate uncaught to the router.

Related errors


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