hcengineering/platform · error

Workspace is closing

Error message

Workspace is closing

What it means

TSessionManager tracks workspaces and their closing state. When handling a request it looks up the workspace by uuid and refuses to serve if the workspace is missing or is in the middle of closing (workspace.closing !== undefined), throwing 'Workspace is closing' so requests are not processed against a shutting-down workspace.

Source

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

    ws: ConnectionSocket,
    operation: (ctx: ClientSessionCtx, rateLimit: RateLimitInfo | undefined) => Promise<void>
  ): Promise<RateLimitInfo | undefined> {
    const rateLimitStatus = this.checkRate(service)
    // If remaining is 0, rate limit is exceeded
    if (rateLimitStatus?.remaining === 0) {
      return await Promise.resolve(rateLimitStatus)
    }

    const source = service.token.extra?.service ?? '🤦‍♂️user'

    // Calculate total number of clients
    const reqId = generateId()

    const st = Date.now()
    try {
      const workspace = this.workspaces.get(service.workspace.uuid)
      if (workspace === undefined || workspace.closing !== undefined) {
        throw new Error('Workspace is closing')
      }

      service.requests.set(reqId, {
        id: reqId,
        params: {},
        start: st
      })

      try {
        await this.counters.withCounter('request', 1, () =>
          workspace.with(async (pipeline) => {
            await requestCtx.with(
              '🧨 ' + method,
              { source, mode: 'rpc' },
              (callTx) =>
                operation(
                  this.createOpContext(callTx, requestCtx, pipeline, reqId, service, ws, rateLimitStatus),
                  rateLimitStatus

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Retry the request after the workspace finishes closing/restarting
  2. Check workspace availability (or wait for a 'ready' signal) before sending requests
  3. Re-open/reconnect the workspace if it was permanently closed
  4. Handle this error client-side as a transient shutdown signal and back off

Example fix

// before
await sendRequest(service, params)
// after
try {
  await sendRequest(service, params)
} catch (e) {
  if (e.message === 'Workspace is closing') {
    await waitForWorkspaceReady(service.workspace.uuid)
    await sendRequest(service, params)
  } else throw e
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

async function sendWithRetry(fn, { retries = 3, baseMs = 500 } = {}) {
  for (let i = 0; ; i++) {
    try {
      return await fn()
    } catch (e) {
      if (e.message === 'Workspace is closing' && i < retries) {
        await new Promise(r => setTimeout(r, baseMs * 2 ** i))
        continue
      }
      throw e
    }
  }
}

Prevention

When it happens

Trigger: Sending a request (with a freshly generated reqId) to a workspace that is currently shutting down, or to a workspace uuid that no longer exists in the manager's map.

Common situations: Client requests racing server shutdown/workspace close; request sent right after workspace was closed or migrated; reconnect attempts during maintenance windows.

Related errors


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