{"record":{"id":"1701c946d39625ca","repo":"thedotmack/claude-mem","slug":"viewer-ui-not-found-at-any-expected-location","errorCode":null,"errorMessage":"Viewer UI not found at any expected location","messagePattern":"Viewer UI not found at any expected location","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/services/worker/http/routes/ViewerRoutes.ts","lineNumber":68,"sourceCode":"\n    app.get('/health', this.handleHealth.bind(this));\n    app.get('/', this.handleViewerUI.bind(this));\n    app.get('/stream', this.handleSSEStream.bind(this));\n  }\n\n  private handleHealth = this.wrapHandler((req: Request, res: Response): void => {\n    const activeSessions = this.sessionManager.getActiveSessionCount();\n\n    res.json({\n      status: 'ok',\n      timestamp: Date.now(),\n      activeSessions\n    });\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  private handleSSEStream = this.wrapHandler((req: Request, res: Response): void => {\n    try {\n      this.dbManager.getSessionStore();\n    } catch (initError: unknown) {\n      if (initError instanceof Error) {\n        logger.warn('HTTP', 'SSE stream requested before DB initialization', {}, initError);\n      }\n      res.status(503).json({ error: 'Service initializing' });\n      return;\n    }\n\n    res.setHeader('Content-Type', 'text/event-stream');\n    res.setHeader('Cache-Control', 'no-cache');","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/services/worker/http/routes/ViewerRoutes.ts#L50-L86","documentation":"Thrown by the GET '/' handler (handleViewerUI) when viewerHtmlBytes is null — meaning at boot neither ui/viewer.html nor plugin/ui/viewer.html under the package root existed (resolvedViewerHtmlPath was null). The HTML is read once and cached at module load; a missing build artifact makes every viewer request fail. A boot warning ('viewer.html not found at any expected location') is logged separately.","triggerScenarios":"Any HTTP GET '/' against the viewer server after the package was installed/built without the viewer HTML present in either candidate location.","commonSituations":"Running from a source tree where the UI build step was skipped; npm package installed without the bundled ui/ directory; plugin/ assembled without copying ui/viewer.html; getPackageRoot() resolving to an unexpected directory.","solutions":["Run the build that produces viewer.html (npm run build-and-sync) so it lands in the expected ui/ directory.","Verify the file exists at <packageRoot>/ui/viewer.html or <packageRoot>/plugin/ui/viewer.html.","Check the boot log 'Cached viewer.html at boot' line to confirm the path resolved; if absent, the 'not found' warning lists the candidates checked.","If getPackageRoot() is wrong, ensure the process cwd / package.json location is correct."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// At boot, surface the missing artifact rather than failing per-request\nimport { existsSync } from 'fs';\nconst ok = VIEWER_HTML_CANDIDATE_PATHS.some(existsSync);\nif (!ok) logger.error('SYSTEM', 'viewer.html missing — rebuild the package');","typeGuard":null,"tryCatchPattern":"// In the route, degrade gracefully instead of throwing\ntry {\n  if (!viewerHtmlBytes) throw new Error('Viewer UI not found');\n  res.send(viewerHtmlBytes);\n} catch {\n  res.status(503).send('Viewer UI not built. Run npm run build-and-sync.');\n}","preventionTips":["Include viewer.html in the published package and CI build artifacts.","Add a CI check that asserts viewer.html exists after build.","Log the resolved candidate paths at boot so a missing file is diagnosable."],"tags":["http","viewer","build-artifact","packaging","routes"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}