{"record":{"id":"b7dd9680fd9342a1","repo":"paperclipai/paperclip","slug":"paperclip-ui-dist-not-found-running-in-api-only","errorCode":null,"errorMessage":"[paperclip] UI dist not found; running in API-only mode","messagePattern":"\\[paperclip\\] UI dist not found; running in API-only mode","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/src/app.ts","lineNumber":716,"sourceCode":"      // SPA fallback. Only for non-asset routes — if the browser asks for\n      // /assets/something.js that doesn't exist, we must NOT serve the HTML\n      // shell: the browser would try to load it as a JavaScript module, fail\n      // with a MIME-type error, and cache that broken response. Return 404\n      // instead. The index.html response itself is no-cache so a subsequent\n      // deploy's updated asset hashes are picked up on next load.\n      app.get(/.*/, (req, res) => {\n        if (req.path.startsWith(\"/assets/\")) {\n          res.status(404).end();\n          return;\n        }\n        res\n          .status(200)\n          .set(\"Content-Type\", \"text/html\")\n          .set(\"Cache-Control\", \"no-cache\")\n          .end(readBrandedStaticIndexHtml(uiDist));\n      });\n    } else {\n      console.warn(\"[paperclip] UI dist not found; running in API-only mode\");\n    }\n  }\n\n  if (opts.uiMode === \"vite-dev\") {\n    const uiRoot = path.resolve(__dirname, \"../../ui\");\n    const publicUiRoot = path.resolve(uiRoot, \"public\");\n    const hmrPort = resolveViteHmrPort(opts.serverPort);\n    const hmrHost = resolveViteHmrHost(opts.bindHost);\n    const hmrProtocol = resolveViteHmrProtocol(process.env.PAPERCLIP_VITE_HMR_PROTOCOL);\n    const hmrServer = createHttpServer((_req, res) => {\n      res.writeHead(426, { \"Content-Type\": \"text/plain\" });\n      res.end(\"Upgrade Required\");\n    });\n    const { createServer: createViteServer } = await import(\"vite\");\n    const vite = await createViteServer({\n      root: uiRoot,\n      appType: \"custom\",\n      server: {","sourceCodeStart":698,"sourceCodeEnd":734,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/server/src/app.ts#L698-L734","documentation":"When the API server starts in static-serving mode (production), it scans candidate directories for a built UI dist containing index.html. If none is found, it warns and serves only /api routes — the board UI is unreachable in the browser while the REST API remains fully functional.","triggerScenarios":"Starting the server in static mode without a prior UI build (ui/dist absent); a packaged deployment that shipped only the server; the dist directory moved or renamed so no candidate contains index.html.","commonSituations":"Running the production entrypoint where only `pnpm dev` was ever used; Docker image built from server-only context; CI artifact missing the UI build; deploying to a host where the relative path to ui/dist changed.","solutions":["Build the UI first: run the repo build (`pnpm build`) so ui/dist with index.html exists next to the server.","Verify the directory the server scans actually contains index.html (candidates are resolved relative to the server build output).","For local development use vite-dev mode (`pnpm dev`) instead of static mode.","If API-only operation is intended, ignore the warning and interact via REST/CLI."],"exampleFix":"# before\npnpm --filter server start\n\n# after\npnpm build && pnpm start","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nimport path from 'node:path';\n\n// Fail fast at deploy time instead of silently serving API-only.\nconst uiDist = path.resolve(__dirname, '../../ui/dist');\nif (!fs.existsSync(path.join(uiDist, 'index.html'))) {\n  throw new Error(`UI dist missing at ${uiDist}; run pnpm build before starting in static mode`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build UI and server in the same CI artifact; assert index.html exists in the package.","Add a post-deploy smoke check that GET / returns HTML (not just /api/health).","Use vite-dev mode only for local dev; static mode requires a prior build.","Alert on this warn in production logs — it means the board UI is down."],"tags":["ui","build-artifact","static-serving","deployment"],"backgroundTag":"missing-build-artifact","analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}