{"record":{"id":"2142e71935a0920d","repo":"overleaf/overleaf","slug":"not-found-connected-client-not-found-res-sendstat","errorCode":null,"errorMessage":"Not Found (connected client not found res.sendStatus(404))","messagePattern":"Not Found \\(connected client not found res\\.sendStatus\\(404\\)\\)","errorType":"http","errorClass":null,"httpStatus":404,"severity":"info","filePath":"services/real-time/app/js/HttpController.js","lineNumber":49,"sourceCode":"      // room names are composed as '<NAMESPACE>/<ROOM>' and the default\n      //  namespace is empty (see comments in RoomManager), just drop the '/'\n      .map(fullRoomPath => fullRoomPath.slice(1))\n    return client\n  },\n\n  getConnectedClients(req, res) {\n    const io = req.app.get('io')\n    const ioClients = io.sockets.clients()\n\n    res.json(ioClients.map(HttpController._getConnectedClientView))\n  },\n\n  getConnectedClient(req, res) {\n    const { client_id: clientId } = req.params\n    const io = req.app.get('io')\n    const ioClient = io.sockets.sockets[clientId]\n    if (!ioClient) {\n      res.sendStatus(404)\n      return\n    }\n    res.json(HttpController._getConnectedClientView(ioClient))\n  },\n}\n","sourceCodeStart":31,"sourceCodeEnd":55,"githubUrl":"https://github.com/overleaf/overleaf/blob/28ad3b03b71cb4311decdcb55c36b33ec10d72db/services/real-time/app/js/HttpController.js#L31-L55","documentation":"GET /api/:project_id/client/:client_id returns HTTP 404 when no socket with the given client_id exists in io.sockets.sockets on this instance. It reports that the connected client view cannot be found — the client is disconnected, the id is wrong, or the socket is homed on another instance.","triggerScenarios":"Querying a client id that has since disconnected, a typo'd/old id, or a socket connected to a different real-time server (multi-instance without sticky routing).","commonSituations":"Debug tooling querying a live client after it refreshed the page; scripts caching client ids across reconnects; load-balanced clusters where the lookup instance is not the owning instance.","solutions":["Re-obtain the current client id from the client (it changes on every reconnect)","Treat 404 as 'not currently connected' and query the right instance or a shared registry","Enable sticky sessions so lookups reach the instance owning the socket","Validate the client_id format before the call to rule out typos"],"exampleFix":"// before\nconst view = await getConnectedClient(projectId, staleClientId)\n// after\nconst res = await getConnectedClient(projectId, clientId)\nif (res.status === 404) {\n  return handleClientOffline(clientId) // refetch id or mark offline\n}\nconst view = await res.json()","handlingStrategy":"fallback","validationCode":"if (!/^[A-Za-z0-9_-]+$/.test(clientId)) throw new Error('malformed client id')","typeGuard":null,"tryCatchPattern":"const res = await fetch(clientUrl)\nif (res.status === 404) {\n  return handleClientOffline(clientId) // refetch id / mark offline\n}\nconst view = await res.json()","preventionTips":["Refresh client ids after every socket reconnect","Route lookups to the instance owning the socket (sticky sessions or shared registry)","Never cache client ids across sessions"],"tags":["http-404","socket-io","internal-api","client-lookup"],"backgroundTag":"client-already-disconnected","analyzedSha":"28ad3b03b71cb4311decdcb55c36b33ec10d72db","analyzedAt":"2026-09-03T02:10:22.807Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T07:17:11.731Z"}