libnyanpasu/clash-nyanpasu · error

failed to kill clash-verge-service.exe

Error message

failed to kill clash-verge-service.exe

What it means

During config migration a PermissionDenied copy triggered an elevated taskkill of clash-verge-service.exe; the command ran but returned a non-zero status, so the migration bails — the service could not be force-killed.

Solutions

  1. Stop clash-verge-service properly (its uninstaller or 'sc stop' with admin rights) and rerun the migration.
  2. Check whether the UAC prompt was declined; retry and accept elevation.
  3. Verify the service process name matches 'clash-verge-service.exe'; adjust the taskkill target if renamed.
  4. As a last resort, reboot (releases file locks) and rerun migration before starting the app.

Example fix

// before
let status = RunasCommand::new("cmd")
    .args(&["/C", "taskkill", "/IM", "clash-verge-service.exe", "/F"])
    .status()?;
if !status.success() {
    anyhow::bail!("failed to kill clash-verge-service.exe")
}
// after
let status = RunasCommand::new("cmd")
    .args(&["/C", "taskkill", "/IM", "clash-verge-service.exe", "/F"])
    .status()?;
if !status.success() {
    anyhow::bail!("failed to kill clash-verge-service.exe; stop the service manually (sc stop clash_verge_service) and retry");
}
Defensive patterns

Strategy: try-catch

Validate before calling

// detect the locking service before attempting migration
let locked = std::process::Command::new("tasklist")
    .args(&["/FI", "IMAGENAME eq clash-verge-service.exe"])
    .output()
    .map(|o| o.status.success() && String::from_utf8_lossy(&o.stdout).contains("clash-verge-service"))
    .unwrap_or(false);
if locked { println!("stop clash-verge-service before migrating"); }

Try / catch

match do_config_migration().await {
    Err(e) if e.to_string().contains("failed to kill clash-verge-service") => {
        eprintln!("stop the service manually (admin): taskkill /IM clash-verge-service.exe /F, then retry");
    }
    other => other,
}

Prevention

When it happens

Trigger: do_config_migration encountering fs_extra PermissionDenied while moving the old app dir, then RunasCommand taskkill /IM clash-verge-service.exe /F failing — service protected, UAC declined, or executable name changed.

Common situations: Upgrading versions while clash-verge-service is running and locking files; user cancels the UAC elevation prompt; service installed under a different name/path.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at backend/tauri/src/utils/init/mod.rs:254

        }
    }
    Ok(None)
}

pub fn do_config_migration(old_app_dir: &PathBuf, app_dir: &PathBuf) -> anyhow::Result<()> {
    let copy_option = CopyOptions::new();
    let copy_option = copy_option.overwrite(true);
    let copy_option = copy_option.content_only(true);
    if let Err(e) = fs_extra::dir::move_dir(old_app_dir, app_dir, &copy_option) {
        match e.kind {
            #[cfg(windows)]
            fs_extra::error::ErrorKind::PermissionDenied => {
                // It seems that clash-verge-service is running, so kill it.
                let status = RunasCommand::new("cmd")
                    .args(&["/C", "taskkill", "/IM", "clash-verge-service.exe", "/F"])
                    .status()?;
                if !status.success() {
                    anyhow::bail!("failed to kill clash-verge-service.exe")
                }
                fs::rename(old_app_dir, app_dir)?;
            }
            _ => return Err(e.into()),
        };
    }
    Ok(())
}

View on GitHub (pinned to f7dbce2997)