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

  1. Verify `path` exists and is executable before spawning (e.g. `path.exists()` plus a graceful error path).
  2. On updates, wait for the old file to be fully written/renamed and file locks released before restarting.
  3. Replace the `.expect` with a fallback: log the error and keep the current process alive instead of exiting.
  4. 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

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


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/5d0e173b4cf81bf9. Report an issue: GitHub.