chenhg5/cc-connect · warning

taskkill failed: %w: %s

Error message

taskkill failed: %w: %s

What it means

signalProcessGroup (agent/claudecode/proc_windows.go:50) stops a Claude Code process tree on Windows by first running 'taskkill /T' to signal the group. If taskkill itself failed (and the failure is not the benign 'no running instance' case), it falls back to cmd.Process.Signal(sig); when that fallback also fails it wraps both as 'taskkill failed: %w: %s' (the taskkill error plus captured output). It indicates the process could not be gracefully signaled.

Source

Thrown at agent/claudecode/proc_windows.go:50

// signalProcessGroup is a graceful best-effort equivalent of forceKillCmd
// on Windows: taskkill without /F asks the target to close cleanly. Falls
// back to cmd.Process.Signal if taskkill is unavailable.
func signalProcessGroup(cmd *exec.Cmd, sig syscall.Signal) error {
	if cmd == nil || cmd.Process == nil {
		return nil
	}
	killCmd := exec.Command("taskkill", "/T", "/PID", strconv.Itoa(cmd.Process.Pid))
	output, err := killCmd.CombinedOutput()
	if err == nil {
		return nil
	}
	if isTaskkillNotRunning(output) {
		return nil
	}
	if killErr := cmd.Process.Signal(sig); killErr == nil || errors.Is(killErr, os.ErrProcessDone) {
		return nil
	}
	return fmt.Errorf("taskkill failed: %w: %s", err, processKillOutput(output))
}

// forceKillCmd taskkill /T /F's the entire descendant tree rooted at cmd.
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 isTaskkillNotRunning(output) {
		return nil
	}
	if killErr := cmd.Process.Kill(); killErr == nil || errors.Is(killErr, os.ErrProcessDone) {
		return nil
	} else {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. If the error's second cause is os.ErrProcessDone or 'access is denied' after exit, treat the stop as successful — the process is already gone.
  2. Retry Stop once after a short delay; transient races between exit and signal are common.
  3. Check captured taskkill output (the %s part) for 'access is denied' and run cc-connect with sufficient privileges or as the process owner.
  4. Verify taskkill.exe is available (it is a standard Windows binary); if removed by hardening policies, use the Unix-style build or a fallback kill path.

Example fix

// before
err := session.Stop(ctx)
if err != nil { log.Error(err) }
// after
err := session.Stop(ctx)
if err != nil {
    var pe *os.ProcessState // or check message
    if errors.Is(err, os.ErrProcessDone) || strings.Contains(err.Error(), "No results") {
        log.Info("process already exited; stop treated as success")
        return nil
    }
    log.Error(err)
}
Defensive patterns

Strategy: retry

Try / catch

if err := session.Stop(ctx); err != nil {
    if errors.Is(err, os.ErrProcessDone) || strings.Contains(err.Error(), "already") {
        return nil // already exited; benign
    }
    time.Sleep(500 * time.Millisecond)
    if err2 := session.Stop(ctx); err2 != nil {
        log.Warn("stop failed; process may linger", "err", err2)
    }
}

Prevention

When it happens

Trigger: Calling Stop/signalProcessGroup on Windows where taskkill exits non-zero with output not matching 'there is no running instance', AND cmd.Process.Signal(sig) returns an error other than os.ErrProcessDone.

Common situations: Process already exited between the taskkill call and the Signal fallback (most common, benign); insufficient privileges to signal a process owned by another user; taskkill not on PATH or blocked by policy; process in a zombie/uninterruptible state.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/e4d207afde78213b. Report an issue: GitHub.