argoproj/argo-workflows · error

failed to load CtrlRoutine: %w

Error message

failed to load CtrlRoutine: %w

What it means

After opening the target process on Windows, signalProcess must locate the CtrlRoutine (the console control-handler entry point in kernelbase.dll) inside the remote process so it can inject a thread that delivers the signal. procCtrlRoutine.Find() failed, so the Windows signal cannot be delivered via remote thread.

Source

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

	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
}

// Following code has been generated using github.com/Microsoft/go-winio/tools/mkwinsyscall and inlined
// for easier usage

// HANDLE CreateRemoteThread(
//
//	HANDLE                 hProcess,

View on GitHub (pinned to 35bff19146)

Solutions

  1. Verify the Windows version supports the CtrlRoutine lookup used by this hcsshim-derived code (upgrade host/OS image if very old)
  2. Check that the target is a normal console process with kernelbase.dll loaded, not a PPL/protected process
  3. Inspect argoexec logs for the wrapped cause; if module enumeration is blocked by container isolation, run with adjusted container privileges
  4. As a fallback, terminate the process group via job-object termination instead of remote-thread signaling
Defensive patterns

Strategy: fallback

Validate before calling

// verify target module enumeration is possible before signaling
if h, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION|windows.PROCESS_TERMINATE, true, pid); err != nil {
    return err
} else { windows.Close(h) }

Try / catch

if err := osspecific.Kill(pid); err != nil && strings.Contains(err.Error(), "failed to load CtrlRoutine") {
    // fall back to hard termination (job-object/TerminateProcess)
}

Prevention

When it happens

Trigger: Kill() → signalProcess() on Windows when the CtrlRoutine address cannot be resolved in the target process — module snapshot/enumeration failure, kernelbase.dll not loaded in the target, or incompatible Windows version where the export layout differs.

Common situations: Targets running in job containers where module enumeration of the remote process is restricted; Windows version mismatch between the resolving code's assumptions and the target OS build; hardened/protected processes refusing cross-process module inspection.

Related errors


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