overleaf/overleaf · info

Not Found (client already disconnected res.sendStatus(404))

Error message

Not Found (client already disconnected res.sendStatus(404))

What it means

The disconnect-client API returns HTTP 404 when the requested socket id is not present in io.sockets.sockets, meaning the client has already disconnected (or never existed on this instance). This is an idempotency signal, not a fault in most cases.

Source

Thrown at services/real-time/app/js/HttpApiController.js:57

  },

  startDrain(req, res) {
    const io = req.app.get('io')
    let rate = req.query.rate || '4'
    rate = parseFloat(rate) || 0
    logger.info({ rate }, 'setting client drain rate')
    DrainManager.startDrain(io, rate)
    res.sendStatus(204)
  },

  disconnectClient(req, res, next) {
    const io = req.app.get('io')
    const { client_id: clientId } = req.params
    const client = io.sockets.sockets[clientId]

    if (!client) {
      logger.debug({ clientId }, 'api: client already disconnected')
      res.sendStatus(404)
      return
    }
    logger.info({ clientId }, 'api: requesting client disconnect')
    client.on('disconnect', () => res.sendStatus(204))
    client.disconnect()
  },
}

View on GitHub (pinned to 28ad3b03b7)

Solutions

  1. Treat 404 as success — the client is already disconnected, which is the desired end state
  2. Verify sticky sessions / direct socket routing if the socket actually exists on another instance
  3. Check for duplicate disconnect calls in the requesting code and deduplicate
  4. If ids are stale, re-fetch the current client id before disconnecting

Example fix

// before
await disconnectClient(clientId) // throws/logs on 404
// after
const res = await disconnectClient(clientId)
if (res.status === 404) return // already disconnected: OK
if (!res.ok) throw new Error(`disconnect failed: ${res.status}`)
Defensive patterns

Strategy: fallback

Validate before calling

// 404 means already disconnected — validate id freshness before calling
if (!clientId || staleCache.has(clientId)) return skipDisconnect()

Try / catch

const res = await fetch(disconnectUrl, { method: 'POST' })
if (res.status === 404) {
  return // already disconnected: treat as success
}
if (!res.ok) throw new Error(`disconnect failed: ${res.status}`)

Prevention

When it happens

Trigger: Calling disconnect-client with a client_id whose socket already closed, whose id is stale, or whose socket lives on a different real-time instance behind the load balancer.

Common situations: Double disconnect requests; disconnecting after the user's tab closed; multi-instance deployments where sticky routing is off and the socket is on another pod; retrying an old client_id after reconnect.

Related errors


AI-assisted analysis of overleaf/overleaf@28ad3b03b7 (2026-09-03). Data as JSON: /api/errors/b115e12cd5519ff8. Report an issue: GitHub.