From 8cf36b862a49aec0028447c033bc1e6f8831e3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E8=89=AF?= <1204183885@qq.com> Date: Tue, 11 Aug 2026 20:58:05 +0800 Subject: [PATCH] fix(desktop): strip Windows verbatim prefix from runtime spawn paths (#8936) Tauri's resource_dir() returns \\?\ verbatim paths on Windows, so the bundled Node executable and the cli-entry.js argument were spawned with that prefix. Node's entry-script resolution cannot handle it and dies with EISDIR before the daemon reports its listening URL, making the app unstartable regardless of workspace. Simplify both paths with dunce before spawning, complementing the workspace-path fix from #8615. Fixes #8929 --- .../desktop-shell/src-tauri/src/runtime.rs | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/desktop-shell/src-tauri/src/runtime.rs b/packages/desktop-shell/src-tauri/src/runtime.rs index 6002cff80a..baf4c00ecb 100644 --- a/packages/desktop-shell/src-tauri/src/runtime.rs +++ b/packages/desktop-shell/src-tauri/src/runtime.rs @@ -162,18 +162,28 @@ impl RuntimeLayout { .join("runtime") .join("qwen-code") }; - let node = if cfg!(windows) { - root.join("node").join("node.exe") - } else { - root.join("node").join("bin").join("node") - }; - let entry = root.join("lib").join("cli-entry.js"); + let (node, entry) = layout_from_root(root); require_file(&node, "Node.js runtime")?; require_file(&entry, "Qwen Code runtime entry")?; Ok(Self { node, entry }) } } +fn layout_from_root(root: PathBuf) -> (PathBuf, PathBuf) { + let node = if cfg!(windows) { + root.join("node").join("node.exe") + } else { + root.join("node").join("bin").join("node") + }; + let entry = root.join("lib").join("cli-entry.js"); + // Tauri's resource_dir() returns `\\?\` verbatim paths on Windows, and + // Node's entry-script resolution cannot handle that prefix (#8929). + ( + dunce::simplified(&node).to_path_buf(), + dunce::simplified(&entry).to_path_buf(), + ) +} + fn require_file(path: &Path, description: &str) -> Result<(), String> { if path.is_file() { return Ok(()); @@ -532,6 +542,8 @@ mod tests { }; #[cfg(unix)] use super::{stop_runtime_handle, wait_for_listening}; + #[cfg(windows)] + use super::layout_from_root; use std::path::Path; #[cfg(windows)] use std::path::PathBuf; @@ -586,6 +598,22 @@ mod tests { assert!(error.contains("unsupported Windows extended-length form")); } + #[cfg(windows)] + #[test] + fn runtime_layout_strips_windows_verbatim_prefix() { + let root = PathBuf::from(r"\\?\C:\Users\user\AppData\Local\Qwen Code Desktop") + .join("runtime") + .join("qwen-code"); + let (node, entry) = layout_from_root(root); + for path in [node, entry] { + let path = path.to_string_lossy(); + assert!( + !path.starts_with("\\\\?\\"), + "runtime path keeps the verbatim prefix: {path}" + ); + } + } + #[test] fn parses_loopback_listening_line() { let url = parse_listening_url(