{"record":{"id":"0865edc12cf66c74","repo":"hcengineering/platform","slug":"err-message-0865ed","errorCode":null,"errorMessage":"err.message","messagePattern":"err\\.message","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"services/export/pod-export/src/server.ts","lineNumber":648,"sourceCode":"          await sourceClient.close()\n          await targetClient.close()\n        }\n      } catch (err: any) {\n        measureCtx.error('Export to workspace request failed:', err)\n        const errorMessage = err instanceof ApiError ? err.message : 'Export to workspace request failed'\n        res.status(err instanceof ApiError ? err.code : 500).send({ message: errorMessage })\n      }\n    })\n  )\n\n  app.use((err: any, _req: any, res: any, _next: any) => {\n    measureCtx.warn(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 {\n    app,\n    close: () => {\n      void storageAdapter.close()\n    }\n  }\n}\n\nasync function createPlatformClient (token: string): Promise<Client> {\n  setMetadata(client.metadata.ClientSocketFactory, (url) => {\n    return new WebSocket(url, {\n      headers: {\n        'User-Agent': process.env.SERVICE_ID\n      }\n    }) as never as ClientSocket\n  })","sourceCodeStart":630,"sourceCodeEnd":666,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/export/pod-export/src/server.ts#L630-L666","documentation":"The export service's global Express error middleware. Errors that are instances of ApiError are returned with their own status code and { code, message }; any other thrown error is reported to Analytics, logged via measureCtx.warn, and returned as 500 with { message: err.message } — or the raw error object if the message is empty. It converts uncaught handler exceptions into JSON error responses.","triggerScenarios":"Any export HTTP request whose handler throws a non-ApiError: decodeToken failures on malformed tokens, storage adapter errors, unexpected exceptions in route logic, or async errors forwarded to next(err).","commonSituations":"Malformed/expired auth tokens hitting decodeToken, storage adapter connectivity problems, programming bugs (undefined access, bad params not caught as ApiError), and 500s with empty error messages returning the raw err object.","solutions":["Inspect the response body: if it has { code, message } it is an ApiError — act on that code; otherwise check server logs (measureCtx.warn) for the full stack.","Ensure the request token is well-formed and current (decodeToken throws on garbage), and re-login if expired.","Check storage adapter configuration/health if the 500 follows an upload/download path.","If the response is the raw error object (no message), check Analytics for the serialized error to find the root cause."],"exampleFix":"// before (empty-message error returned as raw object)\nthrow new Error('')\n// after\nthrow new ApiError(400, 'Missing required parameters')","handlingStrategy":"type-guard","validationCode":"// client: check token shape before calling export APIs\nif (typeof token !== 'string' || token.split('.').length !== 3) {\n  throw new Error('malformed token — obtain a fresh one from login')\n}","typeGuard":"function isApiErrorBody(body: unknown): body is { code: number; message: string } {\n  return typeof body === 'object' && body !== null &&\n    typeof (body as any).code === 'number' && typeof (body as any).message === 'string'\n}","tryCatchPattern":"const res = await exportApi.call(endpoint, payload)\nif (!res.ok) {\n  const body = await res.json().catch(() => null)\n  if (isApiErrorBody(body)) throw new ApiError(body.code, body.message) // server-side ApiError\n  throw new Error(body?.message ?? `export service 500: ${res.status}`)\n}","preventionTips":["Validate tokens are well-formed JWTs before each service call","Differentiate ApiError responses ({ code, message }) from generic 500s in client handling","Re-login on 401-class errors instead of retrying with the same token","Report raw 500 bodies (no message) to support with the request ID/time"],"tags":["http","express","server-error","api"],"backgroundTag":"http-500-unhandled-exception","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}