{"record":{"id":"5c1cc72bd58af3bf","repo":"hcengineering/platform","slug":"err-message-5c1cc7","errorCode":null,"errorMessage":"err.message","messagePattern":"err\\.message","errorType":"http","errorClass":null,"httpStatus":500,"severity":"critical","filePath":"services/notification/pod-notification/src/server.ts","lineNumber":56,"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, onListening?: () => void): Server {\n  const cb = (): void => {\n    if (onListening !== undefined) {\n      onListening()\n    } else {\n      console.log(`Notification service has been started at ${host ?? '*'}:${port}`)\n    }\n  }\n\n  return host !== undefined ? e.listen(port, host, cb) : e.listen(port, cb)\n}\n","sourceCodeStart":38,"sourceCodeEnd":73,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/notification/pod-notification/src/server.ts#L38-L73","documentation":"The pod-notification error middleware's last branch responds HTTP 500 { message: err.message } for any thrown error that is not an ApiError. This is an unexpected server-side exception in a notification endpoint handler (web-push send failure, TypeError, network error) that the framework surfaces as a 500. The client sees only the raw message; the stack lives in the pod's logs.","triggerScenarios":"A handler throws a non-ApiError — e.g. web-push's sendNotification rejects for an invalid/expired subscription, a TypeError from malformed internal state, or an unhandled promise rejection inside the push loop — and catchError forwards it to the error middleware.","commonSituations":"Stale subscriptions whose endpoints now return 4xx/5xx upstream; VAPID keys misconfigured or missing causing web-push to throw; a single bad subscription aborting a batch send; memory/timeout pressure under large subscription lists.","solutions":["Check pod-notification's server logs for the full stack trace; the response body only contains err.message.","Purge invalid/expired PushSubscriptions (upstream 404/410) from storage so sends don't throw.","Verify VAPID key configuration (public/private keys present and valid) before calling the push endpoint.","Harden handler code to catch per-subscription send failures individually and re-throw as ApiError for expected conditions."],"exampleFix":"// before\nawait Promise.all(subs.map(s => webpush.sendNotification(s, payload)))\n// after\nfor (const s of subs) {\n  try {\n    await webpush.sendNotification(s, payload)\n  } catch (e) {\n    if ((e as any).statusCode === 404 || (e as any).statusCode === 410) await removeSubscription(s)\n    else throw new ApiError('PUSH_SEND_FAILED', (e as Error).message)\n  }\n}","handlingStrategy":"try-catch","validationCode":"if (!process.env.VAPID_PUBLIC_KEY || !process.env.VAPID_PRIVATE_KEY) throw new Error('VAPID keys must be configured before calling the push endpoint')","typeGuard":null,"tryCatchPattern":"try {\n  const res = await fetch(pushUrl, opts)\n  if (res.status === 500) {\n    const { message } = await res.json()\n    throw new Error(`notification pod crashed: ${message} (see pod logs)`)\n  }\n} catch (e) { /* backoff/retry transient errors; alert on repeated failures */ }","preventionTips":["Verify VAPID key configuration before enabling push sends","Purge subscriptions that upstream rejects with 404/410 so batch sends don't throw","Apply per-subscription error isolation server-side to keep one bad entry from failing the batch","Alert on 500 rates and keep pod logs retained for stack traces"],"tags":["http-500","unhandled-exception","web-push","server-error"],"backgroundTag":"unhandled-exception-500","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}