argoproj/argo-workflows · error

failed to open process: %w

Error message

failed to open process: %w

What it means

Windows implementation of signalProcess: windows.OpenProcess(PROCESS_TERMINATE, ...) failed, so the executor could not get a handle to the target process in order to terminate/signal it. The underlying Windows error is wrapped — most commonly ERROR_ACCESS_DENIED (5) if the argoexec process lacks rights on the target, or the process already exited.

Source

Thrown at workflow/executor/osspecific/signal_windows.go:69

}

func Wait(process *os.Process) error {
	stat, err := process.Wait()
	if stat.ExitCode() != 0 {
		return errors.NewExitErr(stat.ExitCode())
	}
	return err
}

// signalProcess sends the specified signal to a process.
//
// Code +/- copied from: https://github.com/microsoft/hcsshim/blob/1d69a9c658655b77dd4e5275bff99caad6b38416/internal/jobcontainers/process.go#L251
// License: MIT
// Author: Microsoft
func signalProcess(pid uint32, signal int) error {
	hProc, err := windows.OpenProcess(windows.PROCESS_TERMINATE, true, pid)
	if err != nil {
		return fmt.Errorf("failed to open process: %w", err)
	}
	defer func() {
		_ = windows.Close(hProc)
	}()

	if err := procCtrlRoutine.Find(); err != nil {
		return fmt.Errorf("failed to load CtrlRoutine: %w", err)
	}

	threadHandle, err := createRemoteThread(hProc, nil, 0, procCtrlRoutine.Addr(), uintptr(signal), 0, nil)
	if err != nil {
		return fmt.Errorf("failed to open remote thread in target process %d: %w", pid, err)
	}
	defer func() {
		_ = windows.Close(windows.Handle(threadHandle))
	}()
	return nil
}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Read the wrapped errno: ERROR_ACCESS_DENIED → permissions; ERROR_INVALID_PARAMETER → pid no longer exists
  2. Treat 'process not found' as success (the kill goal was already met) — check if the pid belonged to an already-completed main container
  3. Run argoexec with sufficient privileges in the container (job-container process access)
  4. Retry briefly before failing — there is an inherent race between reading the pid and the process exiting

Example fix

// caller-side tolerance
if err := osspecific.Kill(pid); err != nil {
    if !strings.Contains(err.Error(), "The parameter is incorrect") { // already exited
        return err
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// check the process exists before signaling
if h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid); err != nil {
    // already gone — skip kill
} else { windows.Close(h) }

Try / catch

if err := osspecific.Kill(pid); err != nil {
    if strings.Contains(err.Error(), "The parameter is incorrect") {
        // process already exited; treat as success
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: Kill() calls signalProcess(pid, sig) and the OS refuses the OpenProcess call — bad/already-reaped pid, insufficient privileges, or protected process.

Common situations: Target container process already exited (stale pid) so OpenProcess returns ERROR_INVALID_PARAMETER; argoexec service account lacking PROCESS_TERMINATE rights on the job-container process; Windows job-object/container hardening blocking cross-process access.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/d3ab12355a2371e1. Report an issue: GitHub.