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
- Verify the Windows version supports the CtrlRoutine lookup used by this hcsshim-derived code (upgrade host/OS image if very old)
- Check that the target is a normal console process with kernelbase.dll loaded, not a PPL/protected process
- Inspect argoexec logs for the wrapped cause; if module enumeration is blocked by container isolation, run with adjusted container privileges
- 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
- Ensure the target process is a normal console process with kernelbase.dll loaded
- Match host Windows build with versions the CtrlRoutine lookup supports
- Avoid protected/PPL targets in job containers
- Log the wrapped cause to identify module-enumeration failures
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
- failed to open process: %w
- failed to open remote thread in target process %d: %w
- cannot convert stdin to os.File, it was %T
- failed to read container args file %s: %w
- failed to unmarshal container args: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/80c08c4f0a1d5e97.
Report an issue: GitHub.