fix: error watcher crash — tokio::spawn without runtime context
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 3m4s

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:🧵: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) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-25 12:16:50 -04:00
parent 10e0a6b196
commit 7c2ab2aa60
2 changed files with 16 additions and 9 deletions

View File

@ -153,17 +153,24 @@ pub fn run() {
let (ssh, rdp, sftp, scrollback, watcher) = state; let (ssh, rdp, sftp, scrollback, watcher) = state;
let _ = write_log(&log_file, "Setup: cloned services OK"); let _ = write_log(&log_file, "Setup: cloned services OK");
// Error watcher — std::thread, no tokio needed
let app_handle = app.handle().clone(); 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"); let _ = write_log(&log_file, "Setup: error watcher started");
// MCP HTTP server — needs async runtime
let log_file2 = log_file.clone(); let log_file2 = log_file.clone();
tauri::async_runtime::spawn(async move { let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
match mcp::server::start_mcp_server(ssh, rdp, sftp, scrollback).await { tauri::async_runtime::spawn(async move {
Ok(port) => { let _ = write_log(&log_file2, &format!("MCP server started on localhost:{}", port)); } match mcp::server::start_mcp_server(ssh, rdp, sftp, scrollback).await {
Err(e) => { let _ = write_log(&log_file2, &format!("MCP server FAILED: {}", e)); } 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) => { Err(panic) => {
let msg = if let Some(s) = panic.downcast_ref::<String>() { let msg = if let Some(s) = panic.downcast_ref::<String>() {

View File

@ -99,9 +99,9 @@ pub fn start_error_watcher(
scrollback: ScrollbackRegistry, scrollback: ScrollbackRegistry,
app_handle: AppHandle, app_handle: AppHandle,
) { ) {
tokio::spawn(async move { std::thread::spawn(move || {
loop { 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); let alerts = watcher.scan(&scrollback);
for (session_id, line) in alerts { for (session_id, line) in alerts {