hcengineering/platform · warning
FORCE CLOSE
Error message
FORCE CLOSE
What it means
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.
Source
Thrown at foundations/server/packages/server/src/sessionManager.ts:1441
},
service.binaryMode,
service.useCompression
)
return
}
if (request.id === -1 && request.method === 'hello') {
await requestCtx.with('🧨 handleHello', { source }, (ctx) =>
this.handleHello<S>(request, service, ctx, workspace, ws, requestCtx)
)
return
}
if (request.id === -2 && request.method === 'forceClose') {
// TODO: we chould allow this only for admin or system accounts
let done = false
const wsRef = this.workspaces.get(workspaceId)
if (wsRef?.maintenance ?? false) {
done = true
this.ctx.warn('FORCE CLOSE', { workspace: workspaceId })
// In case of upgrade, we need to force close workspace not in interval handler
await this.forceClose(workspaceId, ws)
}
const forceCloseResponse: Response<any> = {
id: request.id,
result: done
}
await ws.send(requestCtx, forceCloseResponse, service.binaryMode, service.useCompression)
return
}
let rateLimit: RateLimitInfo | undefined
if (request.method !== 'ping') {
rateLimit = this.checkRate(service)
// If remaining is 0, rate limit is exceeded
if (rateLimit?.remaining === 0) {
service.updateLast()
void ws.send(
requestCtx,View on GitHub (pinned to 63e28dc964)
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.
Example fix
// before
if (request.id === -2 && request.method === 'forceClose') {
let done = false
const wsRef = this.workspaces.get(workspaceId)
if (wsRef?.maintenance ?? false) {
done = true
this.ctx.warn('FORCE CLOSE', { workspace: workspaceId })
await this.forceClose(workspaceId, ws)
}
// after
if (request.id === -2 && request.method === 'forceClose') {
if (!isAdminOrSystem(ctx)) return { id: request.id, result: false } // TODO enforced
let done = false
const wsRef = this.workspaces.get(workspaceId)
if (wsRef?.maintenance ?? false) {
done = true
this.ctx.warn('FORCE CLOSE', { workspace: workspaceId })
await this.forceClose(workspaceId, ws)
} Defensive patterns
Strategy: validation
Validate before calling
// Caller side: only send forceClose if maintenance is actually enabled
if (wsRef?.maintenance !== true) return // no forceClose needed
await sendRequest({ id: -2, method: 'forceClose' })
Type guard
function canForceClose (req: { id: number, method: string }, account: { role: string }): boolean {
return req.id === -2 && req.method === 'forceClose' && (account.role === 'admin' || account.role === 'system')
}
Try / catch
try {
const done = await sendRequest({ id: -2, method: 'forceClose' })
} catch (err) {
console.error('forceClose failed', err) // workspace may lack maintenance flag
}
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- platform.status.ConnectionClosed
- ${reply.error}
- The "ws" package is required for NodeWebSocketFactory.
- HTTP error ${res.status}
- result.error
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/fb1e130086a7c4b1.
Report an issue: GitHub.