{"record":{"id":"22622fe57840f8d1","repo":"thedotmack/claude-mem","slug":"viewerunavailable","errorCode":"ViewerUnavailable","errorMessage":"Viewer UI not found at any expected location","messagePattern":"Viewer UI not found at any expected location","errorType":"http","errorClass":null,"httpStatus":503,"severity":"error","filePath":"src/server/runtime/ServerViewerRoutes.ts","lineNumber":57,"sourceCode":"  });\n} else {\n  logger.warn('SYSTEM', 'viewer.html not found for server runtime', {\n    candidates: VIEWER_HTML_CANDIDATE_PATHS,\n  });\n}\n\nexport class ServerViewerRoutes implements RouteHandler {\n  setupRoutes(app: Application): void {\n    const packageRoot = getPackageRoot();\n    // Serve static assets from BOTH the npm-package `ui` dir and the plugin\n    // `plugin/ui` dir, matching the worker's resolution order so the viewer\n    // loads regardless of which layout the server image ships.\n    app.use(express.static(path.join(packageRoot, 'ui')));\n    app.use(express.static(path.join(packageRoot, 'plugin', 'ui')));\n\n    app.get('/', (_req: Request, res: Response) => {\n      if (!viewerHtmlBytes) {\n        res.status(503).json({ error: 'ViewerUnavailable', message: 'Viewer UI not found at any expected location' });\n        return;\n      }\n      res.setHeader('Content-Type', 'text/html; charset=utf-8');\n      res.send(viewerHtmlBytes);\n    });\n  }\n\n  // Exposed for tests: did the build ship a viewer.html the server can serve?\n  static hasViewerHtml(): boolean {\n    return viewerHtmlBytes !== null;\n  }\n}\n","sourceCodeStart":39,"sourceCodeEnd":70,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/server/runtime/ServerViewerRoutes.ts#L39-L70","documentation":"ServerViewerRoutes serves the bundled viewer UI; at module load it probes the npm-package ui/ directory and the plugin/ui directory for viewer.html. If neither ships the file, viewerHtmlBytes stays null and GET / responds 503 ViewerUnavailable. API routes are unaffected — only the web viewer is missing.","triggerScenarios":"Opening the server root URL on a deployment where viewer.html exists under neither <packageRoot>/ui nor <packageRoot>/plugin/ui — custom Docker images, source checkouts without a viewer build, or getPackageRoot() resolving elsewhere (bundled single-file layouts).","commonSituations":"Image build copying only server dist and dropping the ui folder; publish/.npmignore rules excluding built assets; running from a bundled artifact where __dirname-based root resolution lands in a temp dir.","solutions":["Build or reinstall the package so viewer.html exists under ui/ or plugin/ui/","Verify getPackageRoot() resolves to the directory that actually contains the ui folder for your layout","If you do not need the UI, ignore the 503 — JSON API endpoints still work","Use ServerViewerRoutes.hasViewerHtml() in smoke tests to assert the artifact shipped"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"async function viewerAvailable(base: string): Promise<boolean> {\n  const res = await fetch(`${base}/`); // 200 = html, 503 ViewerUnavailable = not shipped\n  return res.status === 200;\n}\n\nif (!(await viewerAvailable(base))) {\n  console.warn('viewer UI not shipped in this build; API still usable');\n}","typeGuard":"function isViewerUnavailable(status: number, body: unknown): body is { error: 'ViewerUnavailable'; message: string } {\n  return status === 503 && typeof body === 'object' && body !== null && (body as { error?: string }).error === 'ViewerUnavailable';\n}","tryCatchPattern":"const res = await fetch(`${base}/`);\nconst body = await res.json().catch(() => ({}));\nif (isViewerUnavailable(res.status, body)) {\n  // packaging defect: rebuild/reinstall so ui/viewer.html ships; API endpoints are unaffected\n  disableUi();\n}","preventionTips":["Assert the ui folder ships in Docker builds (COPY or .dockerignore audit)","Add ServerViewerRoutes.hasViewerHtml() to post-install smoke tests","Verify getPackageRoot() resolution in bundled/single-file deployments"],"tags":["build-artifact","static-assets","http-503","viewer"],"backgroundTag":"missing-build-artifact","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}