{"record":{"id":"671067562d0cb236","repo":"thedotmack/claude-mem","slug":"observation-tv-ui-not-found-at-any-expected-locati","errorCode":null,"errorMessage":"Observation TV UI not found at any expected location","messagePattern":"Observation TV UI not found at any expected location","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/worker/http/routes/ViewerRoutes.ts","lineNumber":196,"sourceCode":"    });\n  });\n\n  private handleViewerUI = this.wrapHandler((req: Request, res: Response): void => {\n    if (!viewerHtmlBytes) {\n      throw new Error('Viewer UI not found at any expected location');\n    }\n    res.setHeader('Content-Type', 'text/html; charset=utf-8');\n    res.send(viewerHtmlBytes);\n  });\n\n  /**\n   * Observation TV: the same /stream the viewer consumes, rendered as a\n   * full-screen fading title card. Static, dependency-free, and served from\n   * the same origin so the EventSource needs no CORS of its own.\n   */\n  private handleTvUI = this.wrapHandler((req: Request, res: Response): void => {\n    if (!tvHtmlBytes) {\n      throw new Error('Observation TV UI not found at any expected location');\n    }\n    res.setHeader('Content-Type', 'text/html; charset=utf-8');\n    res.send(tvHtmlBytes);\n  });\n\n  /**\n   * The target of the restart link in the observer-outage warning\n   * (renderObserverHealthWarning). Serves an INERT page: the restart itself is\n   * the POST /api/admin/restart behind the button, never this GET.\n   *\n   * A GET that restarted the worker would fire from any page that could name\n   * the URL — `<img src=\"http://localhost:PORT/restart\">` on a site the user\n   * happens to open is enough. Same reason the page does not POST on load: an\n   * <iframe> would run that script.\n   *\n   * Requiring a click is NOT on its own enough, though: an attacker who frames\n   * this page can still collect a real click through an overlay. So the route\n   * also refuses to be framed at all — frame-ancestors 'none' for modern","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/8bc631a71a487424b866756e43a6efa4574cc66b/src/services/worker/http/routes/ViewerRoutes.ts#L178-L214","documentation":"The Observation TV route serves a static tv.html cached into memory at module load from <packageRoot>/ui/tv.html or <packageRoot>/plugin/ui/tv.html. If neither file existed when the worker booted, tvHtmlBytes is null, and hitting the TV endpoint throws this error instead of sending a blank or broken page.","triggerScenarios":"Requesting the Observation TV endpoint (handleTvUI route) from a worker whose package install is missing ui/tv.html and plugin/ui/tv.html — typically a broken/partial npm install or a stale published package built before the file was added.","commonSituations":"Corrupted or interrupted npm install leaving assets behind; running from a dev checkout where ui assets were not built/copied; upgrading claude-mem while an old worker process (without tv.html support) is still serving.","solutions":["Reinstall the package cleanly: rm -rf node_modules && npm install (or npx claude-mem@latest) so ui/tv.html is restored","Verify the file exists at <packageRoot>/ui/tv.html (or plugin/ui/tv.html) and restart the claude-mem worker","If installing from source, run the build step that copies ui assets into the package root before starting the worker"],"exampleFix":"// before\n# package missing assets\nls node_modules/claude-mem/ui/   # tv.html absent\n// after\nrm -rf node_modules package-lock.json && npm install\nls node_modules/claude-mem/ui/tv.html && restart claude-mem worker","handlingStrategy":"try-catch","validationCode":"import { existsSync } from 'fs';\nif (!['ui/tv.html', 'plugin/ui/tv.html'].some(p => existsSync(require('path').join(packageRoot, p)))) {\n  throw new Error('tv.html missing from package; reinstall claude-mem');\n}","typeGuard":null,"tryCatchPattern":"app.get('/tv', (req, res) => {\n  if (!tvHtmlBytes) {\n    res.status(503).send('Observation TV assets missing — reinstall claude-mem.');\n    return;\n  }\n  res.type('html').send(tvHtmlBytes);\n});","preventionTips":["After installing or upgrading, verify ui/tv.html exists in the package root before starting the worker","Use clean installs (delete node_modules) to avoid partially extracted packages","When building from source, run the asset-copy/build step before launching the worker"],"tags":["file-not-found","static-assets","worker","ui"],"backgroundTag":"file-not-found","analyzedSha":"8bc631a71a487424b866756e43a6efa4574cc66b","analyzedAt":"2026-09-09T10:47:06.009Z","contentChangedAt":"2026-09-09T10:47:06.009Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}