{"record":{"id":"12f7734912a4b470","repo":"hcengineering/platform","slug":"clients-disconnected-closing-workspace","errorCode":null,"errorMessage":"Clients disconnected. Closing Workspace...","messagePattern":"Clients disconnected\\. Closing Workspace\\.\\.\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"foundations/server/packages/server/src/sessionManager.ts","lineNumber":1224,"sourceCode":"        reason\n      })\n    }\n\n    const sessions = Array.from(workspace.sessions)\n    workspace.sessions.clear()\n\n    const closeS = (s: Session, webSocket: ConnectionSocket): void => {\n      s.workspaceClosed = true\n      if (reason === 'upgrade' || reason === 'force-close') {\n        // Override message handler, to wait for upgrading response from clients.\n        this.sendUpgrade(workspace.context, webSocket, s.binaryMode, s.useCompression)\n      }\n      webSocket.close()\n      this.reconnectIds.delete(s.sessionId)\n    }\n\n    if (LOGGING_ENABLED) {\n      this.ctx.warn('Clients disconnected. Closing Workspace...', {\n        url: workspace.wsId.url,\n        uuid: workspace.wsId.uuid\n      })\n    }\n\n    sessions\n      .filter((it) => it[1].socket.id !== ignoreSocket?.id)\n      .forEach((s) => {\n        closeS(s[1].session, s[1].socket)\n      })\n\n    if (reason !== 'upgrade') {\n      await workspace.close(this.ctx)\n      if (LOGGING_ENABLED) {\n        this.ctx.warn('Workspace closed...', { uuid: workspace.wsId.uuid, url: workspace.wsId.url })\n      }\n    }\n  }","sourceCodeStart":1206,"sourceCodeEnd":1242,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/server/packages/server/src/sessionManager.ts#L1206-L1242","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before shutdown, drain gracefully\nawait sessionManager.stopAcceptingConnections()\nawait waitForInFlightOperations(drainTimeout)","typeGuard":"function hasActiveSessions(workspace: Workspace): boolean {\n  return workspace.sessions.size > 0\n}","tryCatchPattern":"try {\n  await sessionManager.forceClose(wsId)\n} catch (err) {\n  logger.error('workspace close interrupted', { wsId, err })\n  await sessionManager.forceClose(wsId) // retry once to finish teardown\n}","preventionTips":["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."],"tags":["workspace","disconnect","shutdown","sessions"],"backgroundTag":"workspace-closing","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}