{"record":{"id":"218e22067d401f70","repo":"hashicorp/nomad","slug":"error-sending-ctrl-break-event-v","errorCode":null,"errorMessage":"Error sending ctrl-break event: %v","messagePattern":"Error sending ctrl-break event: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"drivers/shared/executor/executor_windows.go","lineNumber":181,"sourceCode":"\n\tresult, err := syscall.WaitForSingleObject(syscall.Handle(handle), 0)\n\n\tswitch result {\n\tcase syscall.WAIT_OBJECT_0:\n\t\treturn nil\n\tcase syscall.WAIT_TIMEOUT:\n\t\t// Process still running.  Just kill it.\n\t\treturn proc.Kill()\n\tdefault:\n\t\treturn os.NewSyscallError(\"WaitForSingleObject\", err)\n\t}\n}\n\n// Send a Ctrl-Break signal for shutting down the process,\nfunc sendCtrlBreak(pid int) error {\n\terr := windows.GenerateConsoleCtrlEvent(syscall.CTRL_BREAK_EVENT, uint32(pid))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"Error sending ctrl-break event: %v\", err)\n\t}\n\treturn nil\n}\n\n// Send the process a Ctrl-Break event, allowing it to shutdown by itself\n// before being Terminate.\nfunc (e *UniversalExecutor) shutdownProcess(_ os.Signal, proc *os.Process) error {\n\tif err := sendCtrlBreak(proc.Pid); err != nil {\n\t\treturn fmt.Errorf(\"executor shutdown error: %v\", err)\n\t}\n\te.logger.Debug(\"sent Ctrl-Break to process\", \"pid\", proc.Pid)\n\n\treturn nil\n}\n","sourceCodeStart":163,"sourceCodeEnd":196,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/drivers/shared/executor/executor_windows.go#L163-L196","documentation":"sendCtrlBreak wraps the failure of windows.GenerateConsoleCtrlEvent, which delivers CTRL_BREAK_EVENT to a task process so it can shut down gracefully. This error means the console control event could not be delivered to the given PID.","triggerScenarios":"Calling sendCtrlBreak with a PID whose process has exited, has no attached console, or was not started with CREATE_NEW_PROCESS_GROUP / shares no console with the caller.","commonSituations":"Shutting down an already-dead task during Nomad kill/stop flows; processes started detached from the console; races where the task exits between signal decision and delivery.","solutions":["Check whether the target process is still alive before/while signaling (accept the error if the process already exited)","Ensure the executor spawned the process with an attached console and new process group so CTRL_BREAK_EVENT is deliverable","Fall back to process termination (Kill) which is the documented next step in shutdownProcess handling","Retry the signal once; transient failures can occur during process teardown"],"exampleFix":"// before\nif err := sendCtrlBreak(proc.Pid); err != nil { return err }\n// after\nif err := sendCtrlBreak(proc.Pid); err != nil {\n    logger.Warn(\"ctrl-break failed, killing\", \"pid\", proc.Pid, \"err\", err)\n    return proc.Kill()\n}","handlingStrategy":"try-catch","validationCode":"alive, err := isProcessAlive(pid); if !alive { skip ctrl-break }","typeGuard":null,"tryCatchPattern":"err := executor.Signal(syscall.SIGBREAK)\nif err != nil && strings.Contains(err.Error(), \"ctrl-break\") {\n    // fall back to force kill / ignore if process already exited\n}","preventionTips":["Verify the task process is alive before signaling","Spawn task processes with CREATE_NEW_PROCESS_GROUP","Treat shutdown-signal errors as best-effort with a terminate fallback"],"tags":["windows","signal","process-shutdown"],"backgroundTag":"console-ctrl-event-failed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}