microsoft/aspire · critical · Win32Exception
Failed to create CLI kill-on-parent-exit job object
Error message
Failed to create CLI kill-on-parent-exit job object
What it means
WindowsConsoleProcessJob implements kill-on-parent-exit by creating a Windows Job Object (CreateJobObjectW) that will terminate assigned children when closed. If the job object cannot be created — an extremely rare kernel-level failure — the constructor throws this Win32Exception with the raw Win32 error and the CLI cannot provide parent-exit protection for console children.
Solutions
- Check NativeErrorCode: ERROR_ACCESS_DENIED points at a security policy — whitelist the Aspire CLI with EDR/AppLocker or run outside the restricted sandbox.
- Retry in a plain console session to determine whether a container/CI sandbox is blocking job objects.
- Free system resources / reboot if the error suggests kernel memory or handle exhaustion.
- Run the child without KillOnParentExit if the environment cannot support job objects, and manage child cleanup explicitly.
- Update the Aspire CLI and report with the exact error code if the environment is otherwise healthy.
Defensive patterns
Strategy: try-catch
Try / catch
try
{
var process = isolatedProcess.Start(startInfo with { KillOnParentExit = true });
}
catch (Win32Exception ex) when (ex.Message == "Failed to create CLI kill-on-parent-exit job object")
{
// Environment blocks job objects — run unrestricted or disable KillOnParentExit and manage cleanup manually
} Prevention
- Run the CLI in environments that permit job objects (not AppLocker/EDR-restricted or exotic sandboxes).
- Whitelist Aspire CLI/DCP executables with security software.
- Avoid launching the CLI inside nested job-restricted wrappers when kill-on-parent-exit matters.
- If unavoidable, fall back to KillOnParentExit=false plus explicit child cleanup in your tooling.
When it happens
Trigger: Constructing WindowsConsoleProcessJob (lazily via WindowsConsoleProcessJob.Shared on first KillOnParentExit spawn) when WindowsProcessInterop.CreateJobObjectW(nint.Zero, null) returns an invalid handle; GetLastWin32Error supplies the code (commonly ERROR_ACCESS_DENIED or out-of-memory/kernel-resource exhaustion).
Common situations: Running in a heavily restricted sandbox, container, or security product that blocks job-object creation; kernel handle/memory exhaustion; nested job limits in exotic hosting environments.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Failed to add job object to process thread attribute list
- Failed to configure CLI kill-on-parent-exit job object…
- Failed to open NUL device
- Failed to open NUL device for stdin
- Failed to set NUL handle inheritance
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/b45797b053ba04b4.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Processes/WindowsConsoleProcessJob.cs:69
private static readonly Lazy<WindowsConsoleProcessJob> s_shared = new(static () => new WindowsConsoleProcessJob());
private readonly SafeFileHandle _jobHandle;
private int _disposed;
/// <summary>
/// The process-wide job, created on first access. Callers that opt into parent-lifetime
/// cleanup use this instead of receiving a job instance, so they cannot forget to supply one.
/// Intentionally never disposed in production: the OS closes the handle at process exit,
/// which is exactly the crash-safety net we want.
/// </summary>
public static WindowsConsoleProcessJob Shared => s_shared.Value;
public WindowsConsoleProcessJob()
{
_jobHandle = WindowsProcessInterop.CreateJobObjectW(nint.Zero, null);
if (_jobHandle.IsInvalid)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to create CLI kill-on-parent-exit job object");
}
try
{
// BREAKAWAY_OK is required so DCP can fork itself with CREATE_BREAKAWAY_FROM_JOB
// and survive the CLI exiting; KILL_ON_JOB_CLOSE catches everything else.
var info = new WindowsProcessInterop.JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
BasicLimitInformation =
{
LimitFlags = WindowsProcessInterop.JobObjectLimitKillOnJobClose
| WindowsProcessInterop.JobObjectLimitBreakawayOk,
},
};
var infoSize = Marshal.SizeOf<WindowsProcessInterop.JOBOBJECT_EXTENDED_LIMIT_INFORMATION>();
var infoPtr = Marshal.AllocHGlobal(infoSize);
tryView on GitHub (pinned to 25830f84bd)