{"record":{"id":"b560ce2e3c2a3e48","repo":"stablyai/orca","slug":"ssh-terminate-reconnect-required","errorCode":"SSH_TERMINATE_RECONNECT_REQUIRED","errorMessage":"${SSH_TERMINATE_RECONNECT_REQUIRED}: SSH relay is not connected; reconnect before terminating remote sessions.","messagePattern":"(.+?): SSH relay is not connected; reconnect before terminating remote sessions\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/ipc/ssh.ts","lineNumber":1331,"sourceCode":"      }\n      for (const ptyId of getPtyIdsForConnection(args.targetId)) {\n        trackPtyId(ptyId, true)\n      }\n      for (const lease of leases) {\n        if (lease.state === 'terminated') {\n          continue\n        }\n        // Why: 'expired' records that reattach gave up, never that the remote shell died — those are\n        // precisely the orphans, so the user's terminate action has to be able to reach them.\n        trackPtyId(lease.ptyId, lease.state !== 'expired')\n      }\n      const ptyIds = Array.from(ptyIdsByRelayId, ([relayPtyId, appPtyId]) => ({\n        relayPtyId,\n        appPtyId\n      }))\n\n      if (ownedRelayIds.size > 0 && !provider) {\n        throw new Error(\n          `${SSH_TERMINATE_RECONNECT_REQUIRED}: SSH relay is not connected; reconnect before terminating remote sessions.`\n        )\n      }\n      const shutdownResults = provider\n        ? await Promise.allSettled(\n            ptyIds.map(({ appPtyId }) =>\n              provider.shutdown(appPtyId, { immediate: true, keepHistory: false })\n            )\n          )\n        : []\n      const shutdownFailures: string[] = []\n      for (const [index, result] of shutdownResults.entries()) {\n        const { appPtyId, relayPtyId } = ptyIds[index]\n        if (result.status !== 'fulfilled' && !isSshPtyNotFoundError(result.reason)) {\n          shutdownFailures.push(\n            `${relayPtyId}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`\n          )\n          continue","sourceCodeStart":1313,"sourceCodeEnd":1349,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/ssh.ts#L1313-L1349","documentation":"Thrown during SSH session termination when ownedRelayIds.size > 0 (there are remote PTY leases the user wants to shut down) but the relay provider is unavailable (provider is falsy). The relay provider is the live connection to the SSH relay that actually issues shutdown commands; without it, the terminate request cannot reach the remote shells. The error code SSH_TERMINATE_RECONNECT_REQUIRED is matched by the renderer (ssh-target-remove.ts, ssh-session-termination.ts) to present a 'reconnect first' UX instead of a generic failure.","triggerScenarios":"User invokes terminate on remote sessions for a targetId whose relay transport dropped (provider undefined) while pty ownership records still exist. The provider may be gone because the connection was severed, the relay reset, or the connectionManager has no live connection object.","commonSituations":"Network blip disconnected the relay while remote terminals were still alive. A relay reset (ssh:resetRelay) cleared the provider but leases persisted. The user is trying to clean up orphaned sessions discovered after a reconnect.","solutions":["Reconnect the SSH target to re-establish the relay provider, then retry the terminate action.","If reconnect is not desired and the remote shells are truly orphaned, accept that they will exit on their own keepalive/timeout and dismiss the terminate attempt.","Renderer: detect the SSH_TERMINATE_RECONNECT_REQUIRED code and prompt the user to reconnect, as the dedicated handlers already do."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const state = await ipcRenderer.invoke('ssh:getState', { targetId })\nif (state.status !== 'connected') {\n  showNotification('Reconnect before terminating remote sessions.')\n  return\n}","typeGuard":null,"tryCatchPattern":"try {\n  await ipcRenderer.invoke('ssh:terminateSessions', { targetId })\n} catch (e) {\n  if ((e as Error).message.includes(SSH_TERMINATE_RECONNECT_REQUIRED)) {\n    promptReconnect(targetId)\n  } else throw e\n}","preventionTips":["Detect SSH_TERMINATE_RECONNECT_REQUIRED in the message and offer a reconnect action.","Disable the terminate control when the relay provider is known to be down.","Track connection health in renderer state to gate destructive remote actions."],"tags":["ssh","relay","session-termination","reconnect","error-code"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}