{"record":{"id":"f57ac0f875310e45","repo":"hcengineering/platform","slug":"err-message-f57ac0","errorCode":null,"errorMessage":"err.message","messagePattern":"err\\.message","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"services/gmail/pod-gmail/src/server.ts","lineNumber":57,"sourceCode":"  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":39,"sourceCodeEnd":70,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/gmail/pod-gmail/src/server.ts#L39-L70","documentation":"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.","triggerScenarios":"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.","commonSituations":"Unvalidated req fields causing TypeErrors, storage layer outages, dependency service errors, bugs introduced after refactors surfacing as opaque 500s.","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"],"exampleFix":"// before\nconst { workspace } = decodeToken(token)\n// after\nlet workspace: string\ntry {\n  ({ workspace } = decodeToken(token))\n} catch (e) {\n  throw new ApiError('INVALID_TOKEN', 'Token could not be decoded')\n}","handlingStrategy":"try-catch","validationCode":"// pre-validate request shape to avoid server-side TypeErrors\nif (typeof body !== 'object' || body == null) throw new Error('request body must be an object')","typeGuard":"function isServerError(body: unknown): body is { message: string } {\n  return typeof body === 'object' && body !== null && typeof (body as any).message === 'string'\n}","tryCatchPattern":"try {\n  const res = await fetch(url, opts)\n  if (res.status >= 500) {\n    const body = await res.json().catch(() => ({}))\n    // server bug or transient outage — retry with backoff, then alert\n    await retryWithBackoff(() => fetch(url, opts))\n  }\n} catch (e) { /* escalate after retries exhausted */ }","preventionTips":["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"],"tags":["http-500","express","unhandled-exception","middleware"],"backgroundTag":"internal-server-error","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}