hcengineering/platform · warning
Clients disconnected. Closing Workspace...
Error message
Clients disconnected. Closing Workspace...
What it means
Warning logged inside TSessionManager.doCloseAll (guarded by LOGGING_ENABLED) when all client sessions of a workspace are being disconnected as part of closing the workspace. Each session socket gets an upgrade notification (for reasons 'upgrade'/'force-close') and is closed; reconnectIds entries are removed. It marks the point where the workspace stops serving clients.
Source
Thrown at foundations/server/packages/server/src/sessionManager.ts:1224
reason
})
}
const sessions = Array.from(workspace.sessions)
workspace.sessions.clear()
const closeS = (s: Session, webSocket: ConnectionSocket): void => {
s.workspaceClosed = true
if (reason === 'upgrade' || reason === 'force-close') {
// Override message handler, to wait for upgrading response from clients.
this.sendUpgrade(workspace.context, webSocket, s.binaryMode, s.useCompression)
}
webSocket.close()
this.reconnectIds.delete(s.sessionId)
}
if (LOGGING_ENABLED) {
this.ctx.warn('Clients disconnected. Closing Workspace...', {
url: workspace.wsId.url,
uuid: workspace.wsId.uuid
})
}
sessions
.filter((it) => it[1].socket.id !== ignoreSocket?.id)
.forEach((s) => {
closeS(s[1].session, s[1].socket)
})
if (reason !== 'upgrade') {
await workspace.close(this.ctx)
if (LOGGING_ENABLED) {
this.ctx.warn('Workspace closed...', { uuid: workspace.wsId.uuid, url: workspace.wsId.url })
}
}
}View on GitHub (pinned to 63e28dc964)
Solutions
- Expected during controlled closes/upgrade — clients reconnect automatically; ensure client code handles close code 99 and the TxModelUpgrade notification.
- If unexpected, trace which caller invoked doCloseAll (forceClose vs shutdown vs upgrade) via the preceding 'closing workspace' info log.
- During shutdown, use graceful drain windows so in-flight client operations complete before close.
Defensive patterns
Strategy: try-catch
Validate before calling
// before shutdown, drain gracefully await sessionManager.stopAcceptingConnections() await waitForInFlightOperations(drainTimeout)
Type guard
function hasActiveSessions(workspace: Workspace): boolean {
return workspace.sessions.size > 0
} Try / catch
try {
await sessionManager.forceClose(wsId)
} catch (err) {
logger.error('workspace close interrupted', { wsId, err })
await sessionManager.forceClose(wsId) // retry once to finish teardown
} Prevention
- Enable LOGGING_ENABLED in production so close events are observable.
- Implement client handlers for close code 99 and the TxModelUpgrade notification to reconnect smoothly.
- Use drain windows during shutdowns so in-flight client requests complete before close.
- Alert on unexpected mass disconnects not tied to a deploy or maintenance event.
When it happens
Trigger: doCloseAll(workspace, code, reason, ignoreSocket?) reached its disconnect phase — invoked from forceClose (reason 'force-close'), shutdown (closeWorkspaces), or an upgrade-triggered close. Fires whenever there is at least one iteration of the close path and LOGGING_ENABLED is true.
Common situations: Server shutdown draining workspaces; force-close of a stuck workspace; an upgrade closing sessions so clients switch to the upgraded model; users seeing a mass disconnect/reconnect wave.
Related errors
- Workspace is closing
- force-close
- Workspace closed...
- Workspace ${options.workspace} not found
- Workspace not found
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/12f7734912a4b470.
Report an issue: GitHub.