windmill-labs/windmill · warning
not found
Error message
not found
What it means
multiplayer/server.mjs's HTTP handler only answers known routes (like the /health-style endpoint that returns { status: 'ok', service: 'multiplayer' }); anything else falls into the else branch which writes 404 and ends with the body 'not found'. It is the server's catch-all for unrecognized HTTP paths.
Source
Thrown at multiplayer/server.mjs:272
// --- HTTP + WebSocket server ---
const server = http.createServer((req, res) => {
// Strip /ws_mp/ prefix if present (when accessed without reverse proxy path stripping)
if (req.url?.startsWith('/ws_mp/')) {
req.url = req.url.slice('/ws_mp'.length)
} else if (req.url === '/ws_mp') {
req.url = '/'
}
console.log(`[${new Date().toISOString()}] HTTP ${req.method} ${req.url} from=${req.socket.remoteAddress}`)
if (req.url === '/' || req.url === '/health') {
res.writeHead(200, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
})
res.end(JSON.stringify({ status: 'ok', service: 'multiplayer' }))
} else {
res.writeHead(404)
res.end('not found')
}
})
const wss = new WebSocketServer({ server })
wss.on('connection', async (ws, req) => {
let docName = req.url?.slice(1).split('?')[0] || 'unknown'
// Strip ws_mp/ prefix if present (when accessed without reverse proxy path stripping)
if (docName.startsWith('ws_mp/')) {
docName = docName.slice('ws_mp/'.length)
}
const clientIp = req.socket.remoteAddress
// Handle ping test — respond and close immediately
if (docName === '__ping__') {View on GitHub (pinned to e474e8803c)
Solutions
- Check the requested path against the routes handled in multiplayer/server.mjs and correct the URL
- If a health check, point it at the implemented status route that returns { status: 'ok', service: 'multiplayer' }
- If a new route is needed, add it to the HTTP handler's if/else chain before the 404 fallback
- Verify the request targets the multiplayer HTTP port, not the WebSocket-only path
Example fix
// before
fetch('http://host:port/healthz') // 404 'not found'
// after
fetch('http://host:port/health') // 200 { status: 'ok', service: 'multiplayer' } Defensive patterns
Strategy: validation
Validate before calling
// verify the route exists before calling
const KNOWN_ROUTES = ['/health'];
if (!KNOWN_ROUTES.includes(pathname)) throw new Error(`unknown multiplayer route: ${pathname}`); Try / catch
const res = await fetch(url);
if (res.status === 404) {
const body = await res.text(); // 'not found'
throw new Error(`multiplayer server has no route for ${url.pathname}: ${body}`);
} Prevention
- Keep the client's base URL/path list in sync with server.mjs routes
- Point health checks at the route returning { status: 'ok', service: 'multiplayer' }
- Don't send HTTP routes to the WebSocket endpoint port
- Add a route table test that curls every documented path
When it happens
Trigger: An HTTP request hits the multiplayer server on a path that does not match any handled route — typo'd URL, wrong base path, request sent to the HTTP port instead of the WebSocket endpoint, or a health-check configured against a nonexistent path.
Common situations: Monitoring/probe pointed at the wrong path; client configured with an outdated route; someone curl-ing the health endpoint with a trailing typo (e.g. /heath); gateway forwarding paths the server does not implement.
Related errors
- No workspace available
- <server error text> || Failed to sign multiplayer session
- Bundle not found
- CSS not found
- Source map not found
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/fe43785248478502.
Report an issue: GitHub.