From b3c3150489489b02716b4270159b070bc4d6d813 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Sun, 1 Nov 2026 01:31:01 +0800 Subject: [PATCH] fix(remote): run commands on the main thread after authorization (#600) Authorization for a remote window resumes on a Tokio worker, so the generated handler ran synchronous commands there, including ones reaching AppKit such as the clipboard check, while local windows run them on the main thread. The handler is now dispatched back to the main thread after authorization, and the request is rejected if dispatch fails. Two app aborts seen in remote windows unwound through this task. The abort was not reproduced and its throw site is unconfirmed. Logging confirmed the thread change. An audit found no remote-only blocking path in the synchronous commands that move to the main thread. --- src-tauri/src/lib.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 006b6aa7a..cc2439dc1 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -95,10 +95,19 @@ pub fn run() { let label = label.to_owned(); let command = request.message.command().to_owned(); let invoke = invoke.clone(); + let app = webview.app_handle().clone(); tauri::async_runtime::spawn(async move { match connection.authorize_command(&label, &command).await { Ok(()) => { - invoke(request); + let resolver = request.resolver.clone(); + // Sync commands must run on the main thread, as they do in local windows. + if let Err(error) = app.run_on_main_thread(move || { + invoke(request); + }) { + resolver.reject(ide_core::IdeError::internal(format!( + "failed to dispatch command on the main thread: {error}" + ))); + } } Err(error) => request.resolver.reject(error), }