{"record":{"id":"d47a2a5695313549","repo":"hcengineering/platform","slug":"closing-workspace-no-users","errorCode":null,"errorMessage":"closing workspace, no users","messagePattern":"closing workspace, no users","errorType":"console","errorClass":null,"httpStatus":null,"severity":"info","filePath":"foundations/server/packages/server/src/sessionManager.ts","lineNumber":323,"sourceCode":"      for (const s of workspace.sessions) {\n        if (this.ticks % (5 * 60 * ticksPerSecond) === workspace.tickHash) {\n          s[1].session.mins5.find = s[1].session.current.find\n          s[1].session.mins5.tx = s[1].session.current.tx\n\n          s[1].session.current = { find: 0, tx: 0 }\n        }\n      }\n\n      // Wait some time for new client to appear before closing workspace.\n      if (\n        !workspace.maintenance &&\n        workspace.sessions.size === 0 &&\n        workspace.closing === undefined &&\n        workspace.workspaceInitCompleted\n      ) {\n        workspace.softShutdown--\n        if (workspace.softShutdown <= 0) {\n          this.ctx.warn('closing workspace, no users', {\n            workspace: workspace.wsId.url,\n            wsId,\n            upgrade: workspace.maintenance\n          })\n          workspace.closing = this.performWorkspaceCloseCheck(workspace)\n        }\n      } else {\n        workspace.softShutdown = workspaceSoftShutdownTicks\n      }\n    }\n  }\n\n  private sendUserWorkspaceStats (): void {\n    const { sys, user, anonymous } = this.calcWorkspaceStats(Array.from(this.sessions.values()))\n\n    let userWorkspaces: number = 0\n    let sysOnlyWorkspaces: number = 0\n","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/server/packages/server/src/sessionManager.ts#L305-L341","documentation":"The SessionManager periodically ticks workspaces; if a workspace has zero sessions, has finished initialization, is not already closing, and its softShutdown countdown reaches zero, the manager logs this warning and starts a workspace close check (soft shutdown). This is normal garbage collection of idle workspaces, freeing memory.","triggerScenarios":"handleWorkspaceTick (from handleTick) sees workspace.sessions.size === 0 and softShutdown decrements to <= 0 — i.e. no user has been connected for the full softShutdown interval.","commonSituations":"Long-lived server accumulating idle workspaces; clients disconnecting and not reconnecting; test environments leaving workspaces open; misread as an error when it's expected lifecycle behavior.","solutions":["No action needed if workspaces are simply idle — they will be re-initialized on next connect","If workspaces close too eagerly, increase the softShutdown timeout configuration","If users report disconnects, check why sessions drop (network, ping timeouts) so the workspace never reaches zero sessions","Monitor for repeated close/init cycles which would indicate a reconnect loop"],"exampleFix":"// before\nsoftShutdown: 100 // ticks\n// after (config, slower close of idle workspaces)\nWORKSPACE_SOFT_SHUTDOWN_TICKS=1000","handlingStrategy":"try-catch","validationCode":"const ws = await getWorkspace(ctx, workspaceId)\nif (ws && ws.sessions.size > 0) console.log('workspace active; will not be closed by tick')","typeGuard":"function isIdleForClose(ws: { sessions: Set<unknown>; closing?: unknown; workspaceInitCompleted: boolean }): boolean {\n  return ws.workspaceInitCompleted && ws.closing === undefined && ws.sessions.size === 0\n}","tryCatchPattern":"try {\n  await client.find(ctx, clazz, query, { workspaceId })\n} catch (err) {\n  if (isWorkspaceClosedError(err)) {\n    await reconnectWorkspace(workspaceId) // triggers re-init\n  } else throw err\n}","preventionTips":["Treat this warning as normal idle lifecycle, not a failure","Handle 'workspace closed' errors on the client by reconnecting (workspace re-initializes automatically)","Increase softShutdown ticks if frequent re-init cost matters","Watch for close/init thrash indicating a client reconnect loop"],"tags":["session","lifecycle","workspace"],"backgroundTag":"idle-workspace-closed","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}