From 7c2ab2aa6001f650bea4d83e9ae7b7d124b880fd Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Wed, 25 Mar 2026 12:16:50 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20error=20watcher=20crash=20=E2=80=94=20to?= =?UTF-8?q?kio::spawn=20without=20runtime=20context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same root cause as the PTY crash (v1.2.6): tokio::spawn called from Tauri setup hook without a tokio runtime guard. Switched error watcher to std::thread::spawn. Also wrapped both error watcher and MCP server spawn in individual catch_unwind blocks so neither can crash the app. Co-Authored-By: Claude Opus 4.6 (1M context) --- src-tauri/src/lib.rs | 21 ++++++++++++++------- src-tauri/src/mcp/error_watcher.rs | 4 ++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 2f2835f..5116e80 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -153,17 +153,24 @@ pub fn run() { let (ssh, rdp, sftp, scrollback, watcher) = state; let _ = write_log(&log_file, "Setup: cloned services OK"); + // Error watcher — std::thread, no tokio needed let app_handle = app.handle().clone(); - mcp::error_watcher::start_error_watcher(watcher, scrollback.clone(), app_handle); + let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + mcp::error_watcher::start_error_watcher(watcher, scrollback.clone(), app_handle); + })); let _ = write_log(&log_file, "Setup: error watcher started"); + // MCP HTTP server — needs async runtime let log_file2 = log_file.clone(); - tauri::async_runtime::spawn(async move { - match mcp::server::start_mcp_server(ssh, rdp, sftp, scrollback).await { - Ok(port) => { let _ = write_log(&log_file2, &format!("MCP server started on localhost:{}", port)); } - Err(e) => { let _ = write_log(&log_file2, &format!("MCP server FAILED: {}", e)); } - } - }); + let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + tauri::async_runtime::spawn(async move { + match mcp::server::start_mcp_server(ssh, rdp, sftp, scrollback).await { + Ok(port) => { let _ = write_log(&log_file2, &format!("MCP server started on localhost:{}", port)); } + Err(e) => { let _ = write_log(&log_file2, &format!("MCP server FAILED: {}", e)); } + } + }); + })); + let _ = write_log(&log_file, "Setup: MCP spawn dispatched"); } Err(panic) => { let msg = if let Some(s) = panic.downcast_ref::() { diff --git a/src-tauri/src/mcp/error_watcher.rs b/src-tauri/src/mcp/error_watcher.rs index e574c05..e5bc6a7 100644 --- a/src-tauri/src/mcp/error_watcher.rs +++ b/src-tauri/src/mcp/error_watcher.rs @@ -99,9 +99,9 @@ pub fn start_error_watcher( scrollback: ScrollbackRegistry, app_handle: AppHandle, ) { - tokio::spawn(async move { + std::thread::spawn(move || { loop { - tokio::time::sleep(std::time::Duration::from_secs(2)).await; + std::thread::sleep(std::time::Duration::from_secs(2)); let alerts = watcher.scan(&scrollback); for (session_id, line) in alerts {