{"record":{"id":"8a37ee64eba25968","repo":"hcengineering/platform","slug":"err-code-8a37ee","errorCode":"err.code","errorMessage":"err.message","messagePattern":"err\\.message","errorType":"http","errorClass":"ApiError","httpStatus":400,"severity":"error","filePath":"services/gmail/pod-gmail/src/server.ts","lineNumber":53,"sourceCode":"\n  app.use(cors())\n  app.use(express.json())\n\n  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(`Gmail 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":35,"sourceCodeEnd":70,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/gmail/pod-gmail/src/server.ts#L35-L70","documentation":"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.","triggerScenarios":"Any endpoint handler (e.g. via catchError wrapper) throws an ApiError — invalid input decoded by controllers, malformed tokens, or business-rule rejections.","commonSituations":"Clients sending payloads that fail controller validation; decodeToken throwing a token-related ApiError; upstream services returning ApiError subclasses that propagate uncaught to the router.","solutions":["Read the returned code field to identify which ApiError subclass fired","Correct the request payload/token that failed validation","Inspect the failing endpoint's controller to see which inputs throw ApiError","Wrap client calls to parse the { code, message } shape for actionable feedback"],"exampleFix":"// before\nconst data = await res.json()\nconsole.log(data)\n// after\nconst data = await res.json()\nif (!res.ok && data.code) {\n  throw new Error(`Request rejected (${data.code}): ${data.message}`)\n}","handlingStrategy":"type-guard","validationCode":"// validate inputs that controllers typically reject with ApiError\nif (!token || !socialId) throw new Error('token and socialId are required')","typeGuard":"function isApiErrorResponse(body: unknown): body is { code: string; message: string } {\n  return typeof body === 'object' && body !== null && 'code' in body && 'message' in body\n}","tryCatchPattern":"try {\n  const res = await fetch(url, opts)\n  const body = await res.json()\n  if (res.status === 400 && isApiErrorResponse(body)) {\n    throw new ApiClientError(body.code, body.message)\n  }\n  return body\n} catch (e) {\n  if (e instanceof ApiClientError) { /* branch on e.code */ }\n  throw e\n}","preventionTips":["Read the `code` field — it identifies the exact validation that failed","Branch on known ApiError codes to give users actionable feedback","Fix the offending payload/token rather than retrying; 400s are deterministic"],"tags":["http-400","express","api-error","bad-request"],"backgroundTag":"api-error-handler","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}