chenhg5/cc-connect · error
taskkill failed: %w: %s; process kill fallback failed: %w
Error message
taskkill failed: %w: %s; process kill fallback failed: %w
What it means
On Windows, forceKillCmd first tries `taskkill /F /T` to terminate the copilot process tree, then falls back to Go's cmd.Process.Kill(). Only if BOTH mechanisms fail does it return "taskkill failed: %w: %s; process kill fallback failed: %w", wrapping the taskkill error, its captured output, and the Process.Kill error.
Source
Thrown at agent/copilot/proc_windows.go:51
func forceKillCmd(cmd *exec.Cmd) error {
if cmd == nil || cmd.Process == nil {
return nil
}
killCmd := exec.Command("taskkill", "/T", "/F", "/PID", strconv.Itoa(cmd.Process.Pid))
output, err := killCmd.CombinedOutput()
if err == nil {
return nil
}
if bytes.Contains(bytes.ToLower(output), []byte("there is no running instance")) {
return nil
}
if bytes.Contains(bytes.ToLower(output), []byte("not found")) {
return nil
}
if killErr := cmd.Process.Kill(); killErr == nil || errors.Is(killErr, os.ErrProcessDone) {
return nil
} else {
return fmt.Errorf("taskkill failed: %w: %s; process kill fallback failed: %w", err, processKillOutput(output), killErr)
}
}
func processKillOutput(output []byte) string {
trimmed := strings.TrimSpace(string(output))
if trimmed == "" {
return "(empty output)"
}
return trimmed
}
View on GitHub (pinned to 4000b2338a)
Solutions
- Check the embedded taskkill output string (via %s) for the root cause — access denied is the most common
- Run cc-connect with sufficient privileges, or verify the process belongs to the same user
- Treat the session as already dead if the process had exited — restart the session rather than retrying the kill
- On non-Windows CI ensure this code path (proc_windows.go) is not being executed unintentionally
Example fix
// before: blindly retrying the same kill
return forceKillCmd(cmd)
// after: accept ErrProcessDone / already-exited as success, else restart session
if errors.Is(killErr, os.ErrProcessDone) { return nil }
slog.Warn("copilot: force kill failed", "err", err); return startNewSession(ctx) Defensive patterns
Strategy: fallback
Validate before calling
// pre-check on Windows before relying on taskkill
if _, err := exec.LookPath("taskkill"); err != nil {
slog.Warn("taskkill unavailable, will rely on Process.Kill fallback")
} Try / catch
err := forceKillCmd(cmd)
if err != nil {
slog.Warn("copilot: force kill failed", "err", err)
// treat the session as dead and rebuild it
return startNewSession(ctx)
} Prevention
- Run the daemon under a user that owns the copilot child processes
- Accept os.ErrProcessDone as a successful kill
- Check the taskkill output embedded in the error for 'Access is denied'
- Test the kill path on a real Windows machine, not only in CI on Linux
When it happens
Trigger: Calling forceKillCmd on a Windows system where taskkill returns a non-"not found" error (bad flags, access denied, process already re-parented) and the subsequent Process.Kill also fails (process already exited, ErrPermission, handle invalid).
Common situations: Insufficient privileges to force-kill a process owned by another user; antivirus blocking taskkill; the process exited between detection and kill so the PID is stale; taskkill.exe missing from PATH while the direct kill also fails.
Related errors
- taskkill failed: %w: %s
- taskkill failed: %w: %s; process kill fallback failed: %w
- another cc-connect instance is already running (PID %d) with
- sudo is not supported on Windows
- run_as_user is not supported on Windows
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/c86c42578d1ace6f.
Report an issue: GitHub.