thedotmack/claude-mem · error · Error
Viewer UI not found at any expected location
Error message
Viewer UI not found at any expected location
What it means
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.
Source
Thrown at src/services/worker/http/routes/ViewerRoutes.ts:68
app.get('/health', this.handleHealth.bind(this));
app.get('/', this.handleViewerUI.bind(this));
app.get('/stream', this.handleSSEStream.bind(this));
}
private handleHealth = this.wrapHandler((req: Request, res: Response): void => {
const activeSessions = this.sessionManager.getActiveSessionCount();
res.json({
status: 'ok',
timestamp: Date.now(),
activeSessions
});
});
private handleViewerUI = this.wrapHandler((req: Request, res: Response): void => {
if (!viewerHtmlBytes) {
throw new Error('Viewer UI not found at any expected location');
}
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.send(viewerHtmlBytes);
});
private handleSSEStream = this.wrapHandler((req: Request, res: Response): void => {
try {
this.dbManager.getSessionStore();
} catch (initError: unknown) {
if (initError instanceof Error) {
logger.warn('HTTP', 'SSE stream requested before DB initialization', {}, initError);
}
res.status(503).json({ error: 'Service initializing' });
return;
}
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');View on GitHub (pinned to d768ba3643)
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.
Defensive patterns
Strategy: validation
Validate before calling
// At boot, surface the missing artifact rather than failing per-request
import { existsSync } from 'fs';
const ok = VIEWER_HTML_CANDIDATE_PATHS.some(existsSync);
if (!ok) logger.error('SYSTEM', 'viewer.html missing — rebuild the package'); Try / catch
// In the route, degrade gracefully instead of throwing
try {
if (!viewerHtmlBytes) throw new Error('Viewer UI not found');
res.send(viewerHtmlBytes);
} catch {
res.status(503).send('Viewer UI not built. Run npm run build-and-sync.');
} Prevention
- 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.
When it happens
Trigger: Any HTTP GET '/' against the viewer server after the package was installed/built without the viewer HTML present in either candidate location.
Common situations: 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.
Related errors
- Failed to get processing status: ${res.status}
- Failed to trigger processing: ${res.status}
- Failed to search: ${searchResponse.status} ${searchResponse.
- Failed to fetch SDK sessions: ${sessionsResponse.status} ${s
- Worker API error (${response.status}): ${errorText}
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/1701c946d39625ca.
Report an issue: GitHub.