diff --git a/apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts b/apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts index e1fb09b08fc..32b30a974cd 100644 --- a/apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts +++ b/apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts @@ -100,6 +100,22 @@ function cancelReconnect(transport: TerminalTransport) { } } +function formatWsEndpoint(wsUrl: string | null): string { + if (!wsUrl) return "unknown endpoint"; + try { + const url = new URL(wsUrl); + return `${url.protocol}//${url.host}${url.pathname}`; + } catch { + return "invalid terminal WebSocket URL"; + } +} + +function formatCloseDetails(event: CloseEvent): string { + const code = event.code || "unknown"; + const reason = event.reason ? `, reason: ${event.reason}` : ""; + return `code: ${code}${reason}`; +} + export function connect( transport: TerminalTransport, terminal: XTerm, @@ -165,17 +181,28 @@ export function connect( } }); - socket.addEventListener("close", () => { + socket.addEventListener("close", (event) => { if (transport.socket !== socket) return; setConnectionState(transport, "closed"); transport.socket = null; + if (!transport._exited && event.code !== 1000) { + const willReconnect = + !transport._reconnectTimer && + Boolean(transport.currentUrl && transport._terminal) && + transport._reconnectAttempt < MAX_RECONNECT_ATTEMPTS; + terminal.writeln( + `\r\n[terminal] WebSocket closed while connected to ${formatWsEndpoint(transport.currentUrl)} (${formatCloseDetails(event)}). ${willReconnect ? "Reconnecting..." : "Max reconnect attempts reached."}`, + ); + } // Auto-reconnect on unexpected close (host-service restart, network blip) scheduleReconnect(transport); }); socket.addEventListener("error", () => { if (transport.socket !== socket) return; - terminal.writeln("\r\n[terminal] websocket error"); + terminal.writeln( + `\r\n[terminal] WebSocket error while connecting to ${formatWsEndpoint(transport.currentUrl)}. Check host-service or relay connectivity.`, + ); }); transport.onDataDisposable?.dispose(); diff --git a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx index c642b0346a7..31329a83de7 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx @@ -155,10 +155,27 @@ export function TerminalPane({ void invalidateTerminalSessionsRef.current({ workspaceId: sessionWorkspaceId, }); + return; } + if (cancelled) return; + const details = result.error + ? `: ${result.error}` + : " for an unknown reason"; + terminalRuntimeRegistry + .getTerminal(terminalId, terminalInstanceId) + ?.writeln( + `\r\n[terminal] Failed to create terminal session${details}`, + ); }) .catch((err) => { console.error("[TerminalPane] ensureSession failed:", err); + if (cancelled) return; + const message = err instanceof Error ? err.message : String(err); + terminalRuntimeRegistry + .getTerminal(terminalId, terminalInstanceId) + ?.writeln( + `\r\n[terminal] terminal.ensureSession request failed: ${message}`, + ); }) .finally(() => { if (cancelled) return; diff --git a/packages/host-service/src/terminal/terminal.ts b/packages/host-service/src/terminal/terminal.ts index a22ba47e731..23cd9288e12 100644 --- a/packages/host-service/src/terminal/terminal.ts +++ b/packages/host-service/src/terminal/terminal.ts @@ -602,10 +602,9 @@ export function registerWorkspaceTerminalRoute({ if (!workspaceId) { sendMessage(ws, { type: "error", - message: - "Session not found. Call terminal.ensureSession first.", + message: `Terminal session "${terminalId}" not found; use terminal.ensureSession or workspaceId.`, }); - ws.close(1011, "Session not found"); + ws.close(1011, "Terminal session not found"); return; }