[BUG] Windows: main window permanently always-on-top (WS_EX_TOPMOST) after silent update relaunch - stealth-relaunch z-order restore anchors to taskbar
> Also reported today via the claude.ai support messenger; posting here as suggested by support so engineering can track it. This is the Windows sibling of #66516, with the mechanism identified and evidenced below.
Date: 2026-08-16
App: Claude Desktop 1.30096.5 (MSIX, Claude_1.30096.5.0_x64__pzs8sxrjxfjjc)
OS: Windows 11 25H2, build 26200.9168
Severity: High annoyance / usability — main window floats above every other application permanently; no UI setting exists to undo it.
---
Summary
After the silent auto-update relaunch ("stealth-relaunch") introduced in the 1.30096.x line, the Claude Desktop main window acquires the WS_EX_TOPMOST extended window style and keeps it permanently. The root cause is the z-order restore step: the app saves the window directly above its own main window as an "anchor" before relaunching, and re-inserts its new window behind that anchor afterwards via SetWindowPos-based moveWindowBehind. When Claude was the top window of the normal band at capture time, the window directly above it is by definition a member of the topmost band — in this incident the Windows taskbar (Shell_TrayWnd). Per documented Win32 z-order semantics, inserting a window among topmost windows makes it topmost itself. Nothing ever clears the flag afterwards.
This is a Windows-side sibling of the open macOS report (anthropics/claude-code#66516, filed 2026-06-09, labeled invalid as wrong repo), but with the exact mechanism identified and evidenced below.
User-visible impact
Claude Desktop stays on top of all other applications while the user works in those other applications. Only workarounds: minimize Claude, or strip the style bit externally (see Workaround). There is no setting in the app to disable it, and a normal user has no way to diagnose or fix this.
Timeline of the incident (all timestamps local, from logs/event log on the affected machine)
| Time | Event | Source |
|---|---|---|
| 2026-08-14 07:48–07:52 | Claude 1.30096.1 installed; first [stealth-relaunch] log lines appear 07:55:19 | AppXDeployment event log; %APPDATA%\Claude\logs\main.log |
| 2026-08-15 07:33/07:53 | MSIX update to 1.30096.5 staged | AppXDeployment event log (event 327) |
| 2026-08-15 09:29:34 | Old instance saves z-order anchor: Saved z-order anchor: 0x10146 (above our 0x104f4) | main.log |
| 2026-08-15 09:30:06 | New instance starts (main claude.exe process group StartTimes 09:30:06–09:30:10); Detected via marker file, Loaded z-order anchor: 0x10146 | process list; main.log |
| 2026-08-15 09:30:07 | Restored z-order: our 0x500aa behind anchor 0x10146 — main window enters topmost band here | main.log |
| 2026-08-16 ~07:13 | Measured main window (hwnd 0x500AA, pid 29048): GetWindowLong(GWL_EXSTYLE) = 0x00280108 → WS_EX_TOPMOST (0x8) set | live measurement |
| 2026-08-16 ~07:14 | Externally cleared via SetWindowPos(HWND_NOTOPMOST) → 0x00280100; stays cleared through the rest of the session (re-checked 07:20:32, 07:25:26) | live measurement |
The window handle in the log (0x500aa = 327850) is the same handle measured with the topmost bit ~22 hours later, and the bit was never re-asserted after external clearing — consistent with a one-shot z-order insertion at relaunch, not a repeating enforcement loop.
Evidence
A. Measurement (no third-party tooling involved)
GetWindowLong(hwnd, GWL_EXSTYLE)on the main window returned0x00280108(WS_EX_TOPMOST = 0x00000008set).- No window-management utilities present or running on the machine (verified: no PowerToys, DeskPins, AutoHotkey, etc.; no PowerToys AlwaysOnTop settings exist).
- No
alwaysOnTop-related key in%APPDATA%\Claude\config.json. - Clearing the bit externally with
SetWindowPos(hwnd, HWND_NOTOPMOST, 0,0,0,0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE)fixed the behavior until the next stealth relaunch.
B. Application log (%APPDATA%\Claude\logs\main.log)
2026-08-15 09:29:34 [info] [stealth-relaunch] Saved z-order anchor: 0x10146 (above our 0x104f4)
2026-08-15 09:30:06 [info] [stealth-relaunch] Detected via marker file (windowVisible=true, windowMinimized=false, otherAppFullScreen=false)
2026-08-15 09:30:06 [info] [stealth-relaunch] Loaded z-order anchor: 0x10146
2026-08-15 09:30:07 [info] [stealth-relaunch] Restored z-order: our 0x500aa behind anchor 0x10146
C. The anchor is the taskbar — a topmost-band window
Walking the current z-order (GetTopWindow/GetWindow) and filtering for windows with WS_EX_TOPMOST shows the topmost band on this machine contains, at the position matching the anchor handle:
hwnd=0x010146 pid=8244 class=Shell_TrayWnd title='' proc=explorer
0x10146 — the exact anchor handle from the log — is Shell_TrayWnd, the Windows taskbar, which is always in the topmost band. The handle is stable across the whole period (explorer.exe did not restart).
Why the taskbar got captured as anchor: the anchor is "the window directly above ours". When the Claude window is the top window of the normal band (very common — the user was just using Claude when the update relaunch hit), the next window up in the global z-order is the bottom of the topmost band, i.e. a topmost window such as the taskbar.
D. Code path (from resources/app.asar of 1.30096.5, minified)
Restore step (invoked at 09:30:07). Note moveWindowBehind(our, anchor) in the native module:
if(dz)try{let t=vr();if(t){let r=Zg(e);t.moveWindowBehind(r,dz),n=!0,
D.info(`[stealth-relaunch] Restored z-order: our %s behind anchor %s`,Qg(r),Qg(dz))}}
The native module @ant/claude-native/claude-native-binding.node (shipped in app.asar.unpacked) contains the strings moveWindowBehind and SetWindowPos — i.e. the reposition is done with SetWindowPos(our, hWndInsertAfter = anchor, ...).
Win32 semantics (SetWindowPos documentation, Remarks): a window can become topmost "by setting its position in the Z order so that it is above any existing topmost windows" — inserting a non-topmost window among the topmost band members transfers WS_EX_TOPMOST onto it. That is exactly what placing the main window directly behind Shell_TrayWnd (with other topmost windows below it) does. No code path subsequently clears the style.
E. Secondary, latent issue in the screenshot wrapper (hardening suggestion)
The screen-capture wrapper in the same bundle (function around the display-capture pipeline, encodeFrameJpeg context) snapshots and restores always-on-top state around each capture:
async function KEn(e){
let t=o.BrowserWindow.getAllWindows().filter(e=>!e.isDestroyed()), n=new Map;
for(let e of t) n.set(e.id, e.isAlwaysOnTop()), e.setContentProtection(!0);
await Dj(UEn); // 50 ms
try { return await e() }
finally {
for(let e of t) e.isDestroyed() || (e.setContentProtection(!1),
n.get(e.id) && !e.isAlwaysOnTop() && e.setAlwaysOnTop(!0, `screen-saver`))
}
}
Two properties make this dangerous once any transient code path (e.g. the computer-use side-panel dock R2n/z2n, which legitimately sets setAlwaysOnTop(true,'screen-saver') on the main window while docked, or the stealth-relaunch defect above) leaves or toggles topmost state concurrently:
- It restores at the fixed
'screen-saver'level regardless of the level it sampled — escalation. - If the sampled
truewas transient and got cleared before thefinallyruns, the wrapper re-asserts topmost permanently — it perpetuates exactly the state this report is about, and can turn a temporary dock state into a sticky one under concurrency.
In this specific incident the log shows no side-panel dock events, so E is not the trigger — it is a second path to the same end state and will keep victims topmost even if they briefly get cleared.
Reproduction (expected)
- Windows 11, Claude Desktop 1.30096.x with pending auto-update.
- Have the Claude main window as the top normal (non-topmost) window — e.g. simply be using Claude. Taskbar present (default).
- Let the silent update relaunch happen (
[stealth-relaunch]path withwindowVisible=true). - After relaunch, inspect the main window:
GetWindowLong(GWL_EXSTYLE) & WS_EX_TOPMOST→ set. The window now covers all other applications until minimized or externally cleared.
Log correlation: the incident always coincides with a [stealth-relaunch] Restored z-order: our <hwnd> behind anchor <hwnd> line where the anchor is a topmost-band window (Shell_TrayWnd being the most likely capture on an idle desktop).
Suggested fixes
- Anchor capture: when saving the z-order anchor, walk down and skip any window with
WS_EX_TOPMOSTset; only anchor to non-topmost windows (or store "we were top of the normal band" and restore withHWND_TOP). - Restore hardening: after
moveWindowBehind, checkGetWindowLong(GWL_EXSTYLE) & WS_EX_TOPMOSTon the own window and clear it withSetWindowPos(HWND_NOTOPMOST)if it was acquired (the binding currently never appliesHWND_NOTOPMOSTin this path). - Screenshot wrapper (E): restore the sampled level instead of forcing
'screen-saver', and skip restoring windows whose topmost state was changed by another subsystem between sample and restore (compare against current intent, not a stale boolean). - Optionally: expose a user-visible escape hatch ("Keep window on top" toggle in the View/Window menu) so any residual state is user-fixable — this also covers the macOS report #66516.
Workaround (for affected users)
PowerShell, run as the logged-in user:
$sig = 'using System;using System.Runtime.InteropServices;public class WT{[DllImport("user32.dll")]public static extern bool SetWindowPos(IntPtr h,IntPtr a,int x,int y,int cx,int cy,uint f);}'
Add-Type -TypeDefinition $sig
$h = (Get-Process claude | Where-Object {$_.MainWindowHandle -ne 0}).MainWindowHandle
[WT]::SetWindowPos($h, [IntPtr](-2), 0, 0, 0, 0, 0x0013) # HWND_NOTOPMOST, NOSIZE|NOMOVE|NOACTIVATE
The state returns after the next silent update relaunch that captures a topmost anchor; a small watcher (Watch-ClaudeTopmost.ps1, checks every 30 s and logs+clears) is in use on the affected machine.
Attachments / raw data available on request
- Full
main.logexcerpts, AppXDeployment event-log entries for the update timeline - Live
GetWindowLongmeasurements before/after clearing - Z-order walk output identifying anchor
0x10146asShell_TrayWnd
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗