{"record":{"id":"868e45638fab9eff","repo":"hcengineering/platform","slug":"high-degraded-sessions","errorCode":null,"errorMessage":"high degraded sessions","messagePattern":"high degraded sessions","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"foundations/server/packages/server/src/sessionManager.ts","lineNumber":548,"sourceCode":"      }\n\n      const hungRequestsPercent = totalRequests > 0 ? (100 * hungRequests) / totalRequests : 0\n      if (hungRequestsPercent > this.hungRequestsFailPercent) {\n        hungSessions += 1\n      }\n    }\n\n    this.ctx.measure('sessions-hung', hungSessions, true)\n\n    const hungSessionsPercent = totalSessions > 0 ? (100 * hungSessions) / totalSessions : 0\n\n    if (hungSessionsPercent > this.hungSessionsFailPercent) {\n      this.ctx.warn('high hung sessions', { hungSessionsPercent })\n      return 'unhealthy'\n    }\n\n    if (hungSessionsPercent > this.hungSessionsWarnPercent) {\n      this.ctx.warn('high degraded sessions', { hungSessionsPercent })\n      return 'degraded'\n    }\n\n    return 'healthy'\n  }\n\n  tickCounter = 0\n\n  @withContext('🧭 get-workspace')\n  async getWorkspace (\n    ctx: MeasureContext,\n    workspaceUuid: WorkspaceUuid,\n    workspaceInfo: LoginInfoWorkspace | undefined,\n    token: Token,\n    ws: ConnectionSocket\n  ): Promise<{ workspace?: Workspace, resp?: GetWorkspaceResponse }> {\n    if (workspaceInfo === undefined) {\n      return { resp: { error: new Error('Workspace not found or not available'), terminate: true } }","sourceCodeStart":530,"sourceCodeEnd":566,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/server/packages/server/src/sessionManager.ts#L530-L566","documentation":"This is a health-check warning (not a thrown exception) emitted by TSessionManager.checkHealth when the percentage of hung sessions exceeds hungSessionsWarnPercent but not hungSessionsFailPercent, causing the server to report status 'degraded'. A session counts as hung when its last request is older than hangSessionTimeoutSeconds, or when a large fraction of its in-flight requests exceed hangRequestTimeoutSeconds. The library emits it so operators can react before the server becomes fully unhealthy.","triggerScenarios":"checkHealth (invoked by health, getStatistics, status endpoints) computes hungSessionsPercent > this.hungSessionsWarnPercent while hungSessionsPercent <= this.hungSessionsFailPercent. Sessions are counted hung if Date.now() - session.lastRequest > hangSessionTimeoutSeconds * 1000, or if >hungRequestsFailPercent of a session's live requests have run longer than hangRequestTimeoutSeconds. System-account sessions are excluded.","commonSituations":"Long-running CPU-heavy operations (builds, migrations) blocking client requests; clients that went idle/disconnected without their sessions being cleaned; a backend dependency (DB, storage) slowing requests past hangRequestTimeoutSeconds; timeouts configured too aggressively for the workload.","solutions":["Inspect which sessions are hung via getStatistics and check their lastRequest timestamps and in-flight request durations.","Check downstream dependencies (database, storage, queue) for latency or lock contention slowing requests.","Tune hangSessionWarnPercent / hangSessionTimeoutSeconds / hangRequestTimeoutSeconds configuration if the workload legitimately has long requests.","Restart or drain the affected sessions/workspaces; if degradation persists, investigate for memory leaks or stuck event-loop tasks."],"exampleFix":"// before: defaults too tight for a migration-heavy workload\nsessionManager = new TSessionManager(ctx, ..., { hangSessionTimeoutSeconds: 30 })\n// after\nsessionManager = new TSessionManager(ctx, ..., { hangSessionTimeoutSeconds: 300, hangRequestTimeoutSeconds: 120 })","handlingStrategy":"validation","validationCode":"const stats = await sessionManager.getStatistics()\nconst health = await sessionManager.checkHealth()\nif (health !== 'healthy') {\n  console.warn('server degraded before accepting load:', health)\n}","typeGuard":"function isHealthy(status: string): boolean {\n  return status === 'healthy'\n}","tryCatchPattern":"try {\n  const status = await sessionManager.checkHealth()\n  if (status !== 'healthy') await alertOps(status)\n} catch (err) {\n  logger.error('health check failed', { err })\n}","preventionTips":["Monitor the sessions-hung metric and alert before warn thresholds are crossed.","Set hangSessionTimeoutSeconds/hangRequestTimeoutSeconds consistent with your real request latency profile.","Watch downstream dependency latency (DB, storage) with dashboards.","Load-test long-running operations to see if they trip the hang detection."],"tags":["health-check","sessions","degraded","monitoring"],"backgroundTag":"server-health-degraded","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}