libnyanpasu/clash-nyanpasu · critical
application failed to start
Error message
application failed to start
What it means
`restart_application` respawns the current executable with the original args and then exits. The `expect` panics if `Command::spawn()` fails, i.e. the OS refused to launch the new process. Because the function was about to exit the app anyway, a failed spawn leaves the user with an app that dies without restarting.
Source
Thrown at backend/tauri/src/utils/help.rs:290
app_handle.exit(0);
}
#[instrument(skip(app_handle))]
pub fn restart_application(app_handle: &AppHandle) {
cleanup_processes(app_handle);
let env = app_handle.env();
let path = current_binary(&env).unwrap();
let arg = std::env::args().collect::<Vec<String>>();
let mut args = vec!["launch".to_string(), "--".to_string()];
// filter out the first arg
if arg.len() > 1 {
args.extend(arg.iter().skip(1).cloned());
}
tracing::info!("restart app: {:#?} with args: {:#?}", path, args);
std::process::Command::new(path)
.args(args)
.spawn()
.expect("application failed to start");
app_handle.exit(0);
std::process::exit(0);
}
#[macro_export]
macro_rules! error {
($result: expr) => {
log::error!(target: "app", "{:?}", $result);
};
}
#[macro_export]
macro_rules! log_err {
($result: expr) => {
if let Err(err) = $result {
log::error!(target: "app", "{:#?}", err);
}
};View on GitHub (pinned to f7dbce2997)
Solutions
- Verify `path` exists and is executable before spawning (e.g. `path.exists()` plus a graceful error path).
- On updates, wait for the old file to be fully written/renamed and file locks released before restarting.
- Replace the `.expect` with a fallback: log the error and keep the current process alive instead of exiting.
- Check OS-specific causes (exec permissions, AV/quarantine, missing exec directory) if it reproduces on user machines.
Example fix
// before
std::process::Command::new(path)
.args(args)
.spawn()
.expect("application failed to start");
app_handle.exit(0);
// after
if let Err(e) = std::process::Command::new(path).args(args).spawn() {
tracing::error!("failed to restart application: {e}");
return; // keep the current instance alive
}
app_handle.exit(0); Defensive patterns
Strategy: try-catch
Validate before calling
if !path.exists() {
tracing::error!("cannot restart: executable missing at {}", path.display());
return;
} Try / catch
match std::process::Command::new(path).args(args).spawn() {
Ok(child) => { /* proceed with exit */ }
Err(e) => {
tracing::error!("restart spawn failed: {e}; staying alive");
return;
}
} Prevention
- Never swap/delete the executable while it may be re-spawned; use atomic rename after full write
- Test restart after auto-update on each target platform
- Keep the current process alive when spawn fails instead of unconditionally exiting
When it happens
Trigger: Calling `restart_application` (e.g. after an update install or a 'restart to apply' action) when the resolved `path` no longer exists (binary replaced/moved during an update), lacks execute permission, or spawn fails due to OS limits/AV interference.
Common situations: Auto-updater swapped the executable file before restart; app installed in a directory whose binary name changed; portable executables on a drive that was unmounted; Windows file lock still held on the old binary.
Related errors
- failed to execute sidecar command
- semaphore should never closed
- version overflow
- prepare() called more than once with different identifiers.
- URL event received before prepare() was called
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/5d0e173b4cf81bf9.
Report an issue: GitHub.