microsoft/aspire · error · Win32Exception

Failed to update process thread attribute list

Error message

Failed to update process thread attribute list

What it means

After initializing the PROC_THREAD_ATTRIBUTE_LIST, the Aspire CLI adds the inheritable-handle list to it via UpdateProcThreadAttribute(ProcThreadAttributeHandleList). This error is thrown when that native update call fails, wrapped with the last Win32 error. It means the child process cannot be started with a restricted, explicit set of inheritable handles.

Solutions

  1. Inspect the inner Win32Exception code (e.g. ERROR_INVALID_HANDLE indicates a handle in the list was closed before the spawn).
  2. Retry the operation — transient handle races can resolve on a second attempt.
  3. Update the Aspire CLI; if reproducible, this indicates a handle-lifetime bug in the interop layer worth reporting.
  4. Ensure no antivirus/EDR policy blocks UpdateProcThreadAttribute usage in your environment.
Defensive patterns

Strategy: retry

Try / catch

catch (Win32Exception ex) when (ex.ErrorCode == unchecked((int)0x80070006)) // ERROR_INVALID_HANDLE
{
    // A handle in the inherit list closed mid-setup; retry the spawn once.
    return RetrySpawnOnce();
}

Prevention

When it happens

Trigger: Spawning a child process on Windows with explicit handle inheritance where UpdateProcThreadAttribute with ProcThreadAttributeHandleList returns false — typically when pinned handle buffer pointers/sizes are invalid or the attribute list was not properly initialized.

Common situations: One or more handles in the inheritable list are invalid or already closed (race between handle collection and process start); memory pressure corrupting pinned buffers; security software interfering with process-creation attribute APIs.

Related errors


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

Appendix: source

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

            try
            {
                var handles = inheritable.ToArray();
                var pinnedHandles = GCHandle.Alloc(handles, GCHandleType.Pinned);
                var pinnedJobHandles = default(GCHandle);
                var jobHandleRefAdded = false;

                try
                {
                    if (!UpdateProcThreadAttribute(
                        attrList,
                        0,
                        ProcThreadAttributeHandleList,
                        pinnedHandles.AddrOfPinnedObject(),
                        (nint)(nint.Size * handles.Length),
                        nint.Zero,
                        nint.Zero))
                    {
                        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");

View on GitHub (pinned to 25830f84bd)