{"record":{"id":"1d1d513ac3b65e4a","repo":"hcengineering/platform","slug":"internal-server-error","errorCode":null,"errorMessage":"Internal Server Error","messagePattern":"Internal Server Error","errorType":"http","errorClass":"HttpError","httpStatus":500,"severity":"error","filePath":"pods/link-preview/src/middleware.ts","lineNumber":72,"sourceCode":"\nexport interface ErrorHandlerOptions {\n  ctx: MeasureContext\n}\n\nexport const errorHandler = (options: ErrorHandlerOptions): ErrorRequestHandler => {\n  const { ctx } = options\n\n  return (err: any, req: Request, res: Response, _next: NextFunction): void => {\n    ctx.error(err.message, { code: err.code, message: err.message })\n\n    if (err instanceof HttpError) {\n      res.status(err.code).json({ message: err.message })\n      return\n    }\n\n    Analytics.handleError(err)\n\n    res.status(500).json({ message: err.message ?? 'Internal Server Error' })\n  }\n}\n","sourceCodeStart":54,"sourceCodeEnd":75,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/pods/link-preview/src/middleware.ts#L54-L75","documentation":"The link-preview pod's errorHandler is the final middleware: for errors that carry a numeric err.code it echoes that status, but for anything else it calls Analytics.handleError and returns HTTP 500 with err.message ?? 'Internal Server Error'. So this message is the fallback for any unexpected exception thrown by link-preview request handling.","triggerScenarios":"Any thrown/uncaught error inside a link-preview route (e.g. parseLinkPreviewDetails throwing on fetch/parse failures) that is not an HttpError with a status code — network failures fetching the URL, HTML parsing exceptions, timeouts.","commonSituations":"Preview target URL unreachable or returns malformed HTML; upstream fetch timeouts; dependency bugs inside the preview parser; config problems surfacing as generic exceptions.","solutions":["Check server logs / Analytics for the underlying error recorded by Analytics.handleError to find the real cause","Verify the target URL is reachable from the server (firewall, DNS, TLS) — not just from your browser","Retry the request; transient fetch/parse failures of the target page are the most common cause","If reproducible for one URL, inspect that page's markup for parser-breaking content and report/upgrade the preview parser"],"exampleFix":"// before\nres.status(500).json({ message: err.message ?? 'Internal Server Error' })\n// after (caller side)\ntry {\n  const preview = await fetchPreview(url)\n} catch (e) {\n  if (e.status === 500) return retryWithBackoff(() => fetchPreview(url))\n  throw e\n}","handlingStrategy":"retry","validationCode":"const parsed = new URL(targetUrl)\nif (!['http:', 'https:'].includes(parsed.protocol)) {\n  throw new Error('unsupported preview target')\n}","typeGuard":"function isHttpError(err: unknown): err is { code: number; message: string } {\n  return typeof err === 'object' && err !== null && typeof (err as any).code === 'number'\n}","tryCatchPattern":"try {\n  preview = await getLinkPreview(url)\n} catch (err) {\n  if (isHttpError(err)) throw err // surface 4xx as-is\n  await backoffRetry(() => getLinkPreview(url), 3)\n}","preventionTips":["Retry 500s with backoff — target-page fetch/parse failures are often transient","Check server-side network access to the preview target, not just client access","Read the Analytics-recorded underlying error before filing a bug","Validate the URL on the client to reduce parse-path exceptions"],"tags":["http-500","unhandled-exception","link-preview"],"backgroundTag":"unhandled-server-exception","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}