paperclipai/paperclip · info
Upgrade Required
Error message
Upgrade Required
What it means
In static (non-dev) mode the server still must bind a placeholder listener on the derived Vite HMR port so the managed-runtime supervisor's readiness check (which waits for both ports) does not kill a healthy server (PAP-18043). That placeholder handler responds 426 Upgrade Required to any request because there is no Vite/HMR to serve in static mode.
Solutions
- Send requests to the main server port, not the derived Vite HMR port
- Update any proxy/monitor to target the application port; treat the HMR port as internal/supervisor-only
- If you need HMR, run in dev mode where a real Vite server binds that port
Example fix
// before curl http://localhost:3101/ # derived HMR port in static mode -> 426 // after curl http://localhost:3100/ # main app port
Defensive patterns
Strategy: type-guard
Validate before calling
const isMainAppPort = (port: number, serverPort: number) => port === serverPort;
if (!isMainAppPort(targetPort, configuredServerPort)) console.warn("target is the placeholder HMR port; expect 426 in static mode"); Type guard
const isUpgradeRequired = (res: Response) => res.status === 426;
Try / catch
const res = await fetch(hmrUrl); if (res.status === 426) { /* wrong port in static mode: use the main server port */ } Prevention
- Point clients, proxies, and monitors at the main server port, never the derived HMR port
- Treat the derived Vite HMR port as supervisor-internal in static mode
- Document the port derivation (serverPort -> hmrPort) so operators avoid probing it
- Run dev mode if you need a live Vite/HMR endpoint on that port
When it happens
Trigger: Hitting the derived HMR port (resolved from the main server port) while the server runs in static mode; a health/probe or browser tab pointed at the HMR port instead of the app port.
Common situations: Configuring a proxy or load balancer to the HMR companion port; opening the wrong port in a browser during local static-mode runs; monitoring checks scanning all listening ports.
Related errors
- )`}
- Announcement request failed
- Anthropic Managed Agents request failed with HTTP
- Artifact download failed: HTTP
- `}
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/6ff41f25ccb5b8fc.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/app.ts:1013
return;
}
res
.status(200)
.set("Content-Type", "text/html")
.set("Cache-Control", "no-cache")
.end(readBrandedStaticIndexHtml(uiDist));
});
} else {
console.warn("[paperclip] UI dist not found; running in API-only mode");
}
if (process.env.PAPERCLIP_MANAGED_RUNTIME_EXPOSURE === "tailscale_https") {
// The managed-runtime supervisor waits for the app port AND its derived
// Vite HMR companion port to bind before publishing the service. Static
// mode has no Vite, so bind the same placeholder listener dev mode uses
// or the supervisor kills a healthy server at the readiness deadline
// (PAP-18043).
const hmrServer = createHttpServer((_req, res) => {
res.writeHead(426, { "Content-Type": "text/plain" });
res.end("Upgrade Required");
});
await listenViteHmrServer(
hmrServer,
resolveViteHmrPort(opts.serverPort),
opts.bindHost,
);
viteHmrServer = hmrServer;
}
}
if (opts.uiMode === "vite-dev") {
const uiRoot = path.resolve(__dirname, "../../ui");
const publicUiRoot = path.resolve(uiRoot, "public");
const hmrPort = resolveViteHmrPort(opts.serverPort);
const hmrHost = resolveViteHmrHost(opts.bindHost);
const hmrProtocol = resolveViteHmrProtocol(
process.env.PAPERCLIP_VITE_HMR_PROTOCOL,View on GitHub (pinned to 3f1d897a7c)