{"record":{"id":"fb1e130086a7c4b1","repo":"hcengineering/platform","slug":"force-close-fb1e13","errorCode":null,"errorMessage":"FORCE CLOSE","messagePattern":"FORCE CLOSE","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"foundations/server/packages/server/src/sessionManager.ts","lineNumber":1441,"sourceCode":"          },\n          service.binaryMode,\n          service.useCompression\n        )\n        return\n      }\n      if (request.id === -1 && request.method === 'hello') {\n        await requestCtx.with('🧨 handleHello', { source }, (ctx) =>\n          this.handleHello<S>(request, service, ctx, workspace, ws, requestCtx)\n        )\n        return\n      }\n      if (request.id === -2 && request.method === 'forceClose') {\n        // TODO: we chould allow this only for admin or system accounts\n        let done = false\n        const wsRef = this.workspaces.get(workspaceId)\n        if (wsRef?.maintenance ?? false) {\n          done = true\n          this.ctx.warn('FORCE CLOSE', { workspace: workspaceId })\n          // In case of upgrade, we need to force close workspace not in interval handler\n          await this.forceClose(workspaceId, ws)\n        }\n        const forceCloseResponse: Response<any> = {\n          id: request.id,\n          result: done\n        }\n        await ws.send(requestCtx, forceCloseResponse, service.binaryMode, service.useCompression)\n        return\n      }\n      let rateLimit: RateLimitInfo | undefined\n      if (request.method !== 'ping') {\n        rateLimit = this.checkRate(service)\n        // If remaining is 0, rate limit is exceeded\n        if (rateLimit?.remaining === 0) {\n          service.updateLast()\n          void ws.send(\n            requestCtx,","sourceCodeStart":1423,"sourceCodeEnd":1459,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/server/packages/server/src/sessionManager.ts#L1423-L1459","documentation":"TSessionManager logs 'FORCE CLOSE' when it receives a special RPC request (id === -2, method 'forceClose') for a workspace currently in maintenance mode; it then calls forceClose(workspaceId, ws) to evict sessions outside the normal interval handler (e.g. during upgrades). The TODO notes it should be restricted to admin/system accounts. Hitting this log means someone explicitly force-closed a maintenance-mode workspace over the protocol.","triggerScenarios":"A client (or tool) sends a JSON-RPC-style request with id -2 and method 'forceClose' targeting a workspaceId whose workspaces entry has maintenance === true.","commonSituations":"Platform upgrades putting workspaces into maintenance and then force-closing them; operators running admin scripts; misbehaving clients sending forceClose (currently unauthenticated - any account could trigger it per the TODO).","solutions":["Expected during upgrades - no action; reconnect after maintenance ends.","Implement the TODO access control: only allow forceClose for admin/system accounts before dispatching to forceClose().","If forceClose fires unexpectedly, audit who can send request id -2 on your deployment.","Ensure clients gracefully handle the forced disconnect (close frame / down event) and retry with backoff after maintenance."],"exampleFix":"// before\nif (request.id === -2 && request.method === 'forceClose') {\n  let done = false\n  const wsRef = this.workspaces.get(workspaceId)\n  if (wsRef?.maintenance ?? false) {\n    done = true\n    this.ctx.warn('FORCE CLOSE', { workspace: workspaceId })\n    await this.forceClose(workspaceId, ws)\n  }\n// after\nif (request.id === -2 && request.method === 'forceClose') {\n  if (!isAdminOrSystem(ctx)) return { id: request.id, result: false } // TODO enforced\n  let done = false\n  const wsRef = this.workspaces.get(workspaceId)\n  if (wsRef?.maintenance ?? false) {\n    done = true\n    this.ctx.warn('FORCE CLOSE', { workspace: workspaceId })\n    await this.forceClose(workspaceId, ws)\n  }","handlingStrategy":"validation","validationCode":"// Caller side: only send forceClose if maintenance is actually enabled\nif (wsRef?.maintenance !== true) return // no forceClose needed\nawait sendRequest({ id: -2, method: 'forceClose' })\n","typeGuard":"function canForceClose (req: { id: number, method: string }, account: { role: string }): boolean {\n  return req.id === -2 && req.method === 'forceClose' && (account.role === 'admin' || account.role === 'system')\n}\n","tryCatchPattern":"try {\n  const done = await sendRequest({ id: -2, method: 'forceClose' })\n} catch (err) {\n  console.error('forceClose failed', err) // workspace may lack maintenance flag\n}\n","preventionTips":["Only send forceClose when the workspace is in maintenance mode.","Enforce admin/system-only authorization on the server (per the TODO).","After forced close, wait for maintenance to end before reconnecting."],"tags":["websocket","maintenance","rpc","security"],"backgroundTag":"workspace-force-close","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}