microsoft/aspire · error · Win32Exception

Failed to add job object to process thread attribute list

Error message

Failed to add job object to process thread attribute list

What it means

The Aspire CLI attaches the kill-on-parent-exit Job Object to a spawned child via UpdateProcThreadAttribute with ProcThreadAttributeJobList so the child is assigned to the job at creation time. This error is thrown when that specific attribute update fails, wrapped with the last Win32 error. The child would then not be bound to the job, so CLI crash-cleanup cannot guarantee its termination.

Solutions

  1. Read the inner Win32Exception code; ERROR_ACCESS_DENIED on the job handle points to outer-job nesting restrictions.
  2. Run the CLI outside restrictive job environments (adjust CI runner configuration or use breakaway).
  3. Retry the operation to rule out a transient SafeHandle lifetime race.
  4. Update/report the issue to the Aspire CLI if reproducible outside sandboxed environments.
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    await runner.StartAsync(exe, args, killOnParentExit: true);
}
catch (Win32Exception ex)
{
    logger.LogWarning("Job-list attribute failed (Win32 error {Code}); child may outlive the CLI.", ex.ErrorCode);
}

Prevention

When it happens

Trigger: Starting a Windows child process with killOnParentExit enabled where UpdateProcThreadAttribute(ProcThreadAttributeJobList) returns false — commonly when the job handle is invalid, the job has JOB_OBJECT_LIMIT_BREAKAWAY_OK restrictions conflicting with the target, or the process is already inside an incompatible job.

Common situations: Running the CLI inside an outer job (CI runners, container runtimes, RDP session jobs) that does not permit nested job assignment; job handle SafeHandle was released before the call (DangerousAddRef race); EDR blocking job-list attributes.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/341d9be9a43bd669. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Cli/Processes/WindowsProcessInterop.cs:431

                        throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error(), "Failed to update process thread attribute list");
                    }

                    if (jobHandle is not null)
                    {
                        jobHandle.DangerousAddRef(ref jobHandleRefAdded);
                        var jobHandles = new[] { jobHandle.DangerousGetHandle() };
                        pinnedJobHandles = GCHandle.Alloc(jobHandles, GCHandleType.Pinned);

                        if (!UpdateProcThreadAttribute(
                            attrList,
                            0,
                            ProcThreadAttributeJobList,
                            pinnedJobHandles.AddrOfPinnedObject(),
                            (nint)(nint.Size * jobHandles.Length),
                            nint.Zero,
                            nint.Zero))
                        {
                            throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error(), "Failed to add job object to process thread attribute list");
                        }
                    }

                    var si = new STARTUPINFOEX
                    {
                        cb = Marshal.SizeOf<STARTUPINFOEX>(),
                        dwFlags = StartfUseStdHandles | StartfUseShowWindow,
                        hStdInput = stdio.Stdin,
                        hStdOutput = stdio.Stdout,
                        hStdError = stdio.Stderr,
                        lpAttributeList = attrList,
                        // CREATE_NO_WINDOW is ignored with CREATE_NEW_CONSOLE; SW_HIDE keeps
                        // the new console window off-screen.
                        wShowWindow = ShowWindowHide,
                    };

                    var commandLine = BuildCommandLine(fileName, arguments);

View on GitHub (pinned to 25830f84bd)