{"record":{"id":"42874b6fa824fe41","repo":"hcengineering/platform","slug":"err-code","errorCode":"err.code","errorMessage":"err.message (500 fallback sends err object when message is empty)","messagePattern":"err\\.message \\(500 fallback sends err object when message is empty\\)","errorType":"http","errorClass":"ApiError","httpStatus":500,"severity":"warning","filePath":"services/ai-bot/pod-ai-bot/src/server/server.ts","lineNumber":192,"sourceCode":"      const resp = await controller.getLoveIdentity(roomName)\n\n      if (resp === undefined) {\n        throw new ApiError(404)\n      }\n\n      res.status(200)\n      res.json(resp)\n    })\n  )\n\n  app.use((err: any, _req: any, res: any, _next: any) => {\n    console.log(err)\n    if (err instanceof ApiError) {\n      res.status(err.code).send({ code: err.code, message: err.message })\n      return\n    }\n\n    res.status(500).send(err.message?.length > 0 ? { message: err.message } : err)\n  })\n\n  return app\n}\n\nexport function listen (e: Express, port: number, host?: string): Server {\n  const cb = (): void => {\n    console.log(`AI 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":174,"sourceCodeEnd":205,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/ai-bot/pod-ai-bot/src/server/server.ts#L174-L205","documentation":"The ai-bot server's global Express error handler logs the error, returns ApiError instances with their own code and message, and for all other errors responds 500 with {message: err.message} — or the raw err object when message is empty. Seeing the raw error object in the response means a non-ApiError with an empty/absent message escaped a handler.","triggerScenarios":"Any unhandled exception thrown in a route that is not an ApiError and whose err.message is empty or undefined — e.g. Error(''), non-Error objects thrown, or fetch/network errors without messages.","commonSituations":"Upstream HTTP client rejections yielding message-less errors; throwing plain objects or strings; bugs producing empty Error instances; unhandled promise rejections reaching the error middleware.","solutions":["Read the raw error object in the 500 response (or console.log output) to identify the true failure.","Wrap known failure paths in ApiError with a code and message so clients get structured errors.","Ensure thrown values are Error instances with descriptive messages.","Add middleware-level logging/serialization for non-Error throws."],"exampleFix":"// before\nthrow { status: 502 }\n// after\nthrow new ApiError(502, 'upstream service unavailable')","handlingStrategy":"try-catch","validationCode":"// Client: detect the raw-object 500 shape before using it\nfunction extractServerMessage(body: unknown): string {\n  if (typeof body === 'object' && body !== null && 'message' in body && typeof (body as any).message === 'string') return (body as any).message\n  return 'unstructured server error: ' + JSON.stringify(body)\n}","typeGuard":"function isApiErrorPayload(x: unknown): x is { code: number; message: string } {\n  return typeof x === 'object' && x !== null && typeof (x as any).code === 'number' && typeof (x as any).message === 'string'\n}","tryCatchPattern":"try {\n  return await callAiBot(req)\n} catch (err) {\n  if (err instanceof ApiError) {\n    return res.status(err.code).json({ code: err.code, message: err.message })\n  }\n  logger.error({ err }, 'unhandled ai-bot error')\n  return res.status(500).json({ message: err instanceof Error && err.message ? err.message : 'internal error' })\n}","preventionTips":["Throw ApiError (with code + message) for every anticipated failure path.","Never throw plain objects or strings; always throw Error subclasses.","Await all promises in async routes so rejections carry proper messages.","Add a final error middleware that serializes unknown throws into structured errors."],"tags":["http-500","error-handling","express"],"backgroundTag":"unhandled-server-error","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}