Quick answer
ERROR_CTX_CLOSE_PENDING (0x00001B5F) isn't a crash — it's Windows telling you the session is already in the middle of shutting down. Stop hammering it, wait for the async close to finish, or terminate the session externally if it's stuck.
What's actually happening
This code lives in the Terminal Services / Session Manager family of Win32 errors. When you call something like WTSLogoffSession, WTSDisconnectSession, or the older Win32 Close path on a session handle, Windows doesn't tear the session down synchronously. It queues a close request and returns immediately. The actual teardown — flushing user profile registry hives, unloading per-session drivers, notifying subscribers on the session change notification bus — can take anywhere from a few hundred milliseconds to several seconds.
If any code (yours, a service, or a management tool) tries to close that same session again before the first close has fully landed, Windows returns ERROR_CTX_CLOSE_PENDING. Translated: "I hear you, but there's already a close in flight. Piss off and wait."
Real-world trigger I've hit repeatedly: a PowerShell script that loops quser output and calls logoff on every disconnected session, then immediately tries to query session state again to confirm. The first logoff kicks off fine, the confirmation query races the teardown, and you get 0x1B5F sprayed across the console. Same thing happens with third-party RDS management dashboards that poll every second — they catch the window between "close initiated" and "close complete," and the API throws this at them.
It also shows up in Remote Desktop Services farms when a session broker tries to drain a session to a target host while the session is already being closed by a logoff GPO. Two actors, one session, race condition.
Fix it
- Stop retrying for 30 seconds. This is the actual fix for 90% of cases. The close will complete on its own. Open an elevated
cmd.exeand runquery sessionto watch the state transition fromDiscorActiveto nothing. - Verify the session is genuinely stuck. If it's still listed after 60 seconds, the teardown is hung. Check the event log first:
Look for Event ID 23, 24, or 25. ID 23 is a session logoff — if you see it fire but the session remains, a user-mode process is blocking the profile unload.Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TerminalServices-LocalSessionManager/Operational'; StartTime=(Get-Date).AddMinutes(-10)} | Format-List - Force the session down. From an elevated prompt:
Ifquery session
logoff <SessionID> /vlogoffreturns 0x1B5F too, the Session Manager is still holding the close request. Give it another 30 seconds, then move to step 4. - Kill the blocking user process. The usual culprit is a sync client (OneDrive, a VPN tray app, an antivirus agent) that won't release its handle to
NTUSER.DAT. Find it with:
Kill the offender, then retryGet-Process | Where-Object { $_.SessionId -eq <SessionID> } | Select-Object Name, Id, SessionIdlogoff. - Restart TermService as a last resort. This will disconnect every user on the box, so do it in a maintenance window:
Windows will clear the pending close queue and rebuild session state on next connection. Any session that was mid-close is abandoned and its profile hive may need aRestart-Service TermService -Forcechkdskpass on the next boot.
If that doesn't work
Sometimes the Session Manager itself is wedged and won't process the queue. Check the SMSS (Session Manager Subsystem) — if it's pegged on CPU or unresponsive to query session, you're looking at a reboot. Before you do that, dump the state for postmortem:
Get-WinEvent -LogName System -MaxEvents 200 | Where-Object { $_.ProviderName -match 'Session|Terminal' }
Second alternative: check for a stale WTSQuerySessionInformation handle held open by a service that crashed but didn't release its reference. Restarting the calling service (usually your RDS broker or a monitoring agent) clears it without touching TermService.
Third: on RDS farms specifically, check the Connection Broker database for a stuck session record. Get-RDUserSession from the Connection Broker's PowerShell module will show orphaned entries that block new logons to the same session ID.
Prevention
Don't poll session state in a tight loop. If you're scripting session cleanup, add a wait: after calling logoff, sleep 2–3 seconds before checking the result, and treat 0x1B5F as "in progress," not "failed." In C/C++ code, check the return of WTSLogoffSession and only treat 0x1B5F as informational. Also watch your antivirus — a number of enterprise AV products hold handles on user registry hives during logoff and stretch the close window from 500ms to 8+ seconds. Excluding C:\Users\*\NTUSER.DAT* from real-time scanning cuts this error to near zero on busy RDS hosts.