hcengineering/platform · info

Closed workspace

Error message

Closed workspace

What it means

TSessionManager logs 'Closed workspace' after successfully calling workspace.close(this.ctx), deleting the workspace from this.workspaces, ending its context, and publishing a workspace down event. It confirms an idle (zero-session) workspace was fully torn down. It is informational, and marks the point after which pending operations targeting that workspace will fail because it no longer exists.

Solutions

  1. No action needed - this is a success-path log confirming cleanup.
  2. If clients still need the workspace, reconnect before it idles out or keep a session open.
  3. Verify clients handle workspace-down events (from workspaceProducer) by reattaching/reopening the workspace.
  4. If workspace.close throws, check the catch block (Analytics.handleError) to see the underlying failure.
Defensive patterns

Strategy: fallback

Validate before calling

// After seeing 'Closed workspace', confirm the workspace is gone before reusing the reference
if (!manager.hasWorkspace(uuid)) {
  await reopenWorkspace(uuid)
}

Type guard

function workspaceExists (workspaces: Map<string, unknown>, uuid: string): boolean {
  return workspaces.has(uuid)
}

Prevention

When it happens

Trigger: The close check path in performWorkspaceCloseCheck completes: workspace.sessions.size === 0, workspace.close resolves without throwing, and LOGGING_ENABLED is true.

Common situations: Operator logs showing workspace shutdown during maintenance or idle cleanup; clients attempting to reconnect to a workspace that was just torn down; server load reduction closing unused workspaces.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/90729bb10380d19e. Report an issue: GitHub.

Appendix: source

Thrown at foundations/server/packages/server/src/sessionManager.ts:1280

    await this.workspaceProducer.close()
    await this.usersProducer.close()
  }

  private async performWorkspaceCloseCheck (workspace: Workspace): Promise<void> {
    const uuid = workspace.wsId.uuid
    const logParams = { uuid, url: workspace.wsId.url }
    if (workspace.sessions.size === 0) {
      if (LOGGING_ENABLED) {
        this.ctx.warn('no sessions for workspace', logParams)
      }
      try {
        if (workspace.sessions.size === 0) {
          await workspace.close(this.ctx)

          this.workspaces.delete(uuid)
          workspace.context.end()
          if (LOGGING_ENABLED) {
            this.ctx.warn('Closed workspace', logParams)
          }

          await this.workspaceProducer.send(this.ctx, workspace.wsId.uuid, [workspaceEvents.down()])
        }
      } catch (err: any) {
        Analytics.handleError(err)
        this.workspaces.delete(uuid)
        if (LOGGING_ENABLED) {
          this.ctx.error('failed', { ...logParams, error: err })
        }
      }
    } else {
      if (LOGGING_ENABLED) {
        this.ctx.info('few sessions for workspace, close skipped', {
          ...logParams,
          sessions: workspace.sessions.size
        })
      }

View on GitHub (pinned to 63e28dc964)