Windows: main window intermittently gains WS_EX_TOPMOST (breaks Alt+Tab) — captured correlation with LocalSessions.setFocusedSession
Summary
On Windows, the Claude desktop app's main window intermittently acquires theWS_EX_TOPMOST extended window style. Alt+Tab still switches windows correctly,
but Claude stays painted above whatever gains focus, so it presents to users as
"Alt+Tab is broken / I can't get out of Claude." There is no user-facing control
to clear it, and the state is not persisted, so the only user-accessible recovery
is restarting the app — after which it recurs.
This is the same underlying behavior as #89467, #85891, #88093, #88359 and
(closed as duplicate) #87895. I am filing separately because I have something
those threads do not: instrumented capture of the app's state at the moment the
flag is applied, plus a set of eliminated hypotheses. Happy for this to be
merged into #89467 — it is the properly triaged one.
Environment
| | |
|---|---|
| OS | Windows 11 Home 10.0.26200 |
| Display | single internal panel, 1536x960, scaleFactor 1.25 |
| Versions affected | 1.37937.1 and 1.40609.1 (behavior survived the update) |
| Package | Claude_1.40609.1.0_x64__pzs8sxrjxfjjc (MSIX) |
The flag
When active, the main window's ExStyle reads 0x00280108:WS_EX_NOREDIRECTIONBITMAP | WS_EX_LAYERED | WS_EX_WINDOWEDGE | WS_EX_TOPMOST.
The offending bit is WS_EX_TOPMOST (0x8). Clearing it withSetWindowPos(hwnd, HWND_NOTOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE)
fully restores expected behavior, immediately, with no other side effects and no
restart required.
The affected window is the ordinary maximized main window. I confirmed this by
enumerating every top-level window on the process — it was the only visible
one, so this is not a popout or floating overlay being misidentified.
Occurrences
| Date | PID | HWND | ExStyle |
|---|---|---|---|
| 2026-08-26, by 18:54 | 38540 | 0x4018C | 0x00280108 |
| 2026-09-01, by 16:10 | 29360 | 0x901FE | 0x00280108 |
| 2026-09-02, 07:58:39 | 29360 | 0x901FE | 0x00280108 |
| 2026-09-05 | 47212 | 0x60336 | 0x00280108 |
Lead: correlation with session focus switching
I ran a 20-second poller that snapshots main.log whenever it observes the flag.
The 2026-09-02 07:58:39 capture shows the following in the same second the pin was
detected (session ids shortened):
07:58:37 [info] LocalSessions.sendMessage: sessionId=local_fc8e...
07:58:37 [info] Sending message to session local_fc8e...
07:58:39 [info] [CCD] LocalSessions.setFocusedSession: sessionId=null
07:58:39 [info] [WarmLifecycle:preview] Warming up session local_d7ba...
07:58:39 [info] [CCD] LocalSessions.setFocusedSession: sessionId=local_d7ba...
07:58:39 [info] LocalSessions.startShellPty: sessionId=local_d7ba...
The 2026-09-01 occurrence shows the same shape: setFocusedSession calls
accompanied by Failed to set title bar overlay, this is probably expected —
a window-chrome operation — shortly before the pin was observed.
Stated as a lead, not a conclusion. The poll interval means the flag may have
been set up to 20s before the snapshot, so this is temporal correlation from a
small number of captures, not a proven cause. But switching the focused session
appears to re-apply window properties, and the app bundle contains a re-apply
path along the lines of:
let t = this.currentProps.alwaysOnTopLevel;
t && e.setAlwaysOnTop(true, t);
If alwaysOnTopLevel can be non-empty for the main window at that point — for
instance inherited or leaked from an overlay's props — that path would produce
exactly this symptom.
For context on which windows are supposed to be topmost: the quick-entry window
(pop-up-menu level), and the dictation widget and screenshot overlay
(screen-saver level). All are frameless. The main window is not among them.
Ruled out, with evidence
- Not persisted. No pin-shaped key (
alwaysOnTop,isPinned,onTop,
stayOnTop, floating) in config.json, Preferences, Local Storage, or
Session Storage under %APPDATA%\Claude. Applied fresh at runtime.
- Not user-triggered. The app ships no always-on-top control; searching its
string resources for "on top" / "pin" / "float" returns nothing.
- Not a third-party pinning utility. No PowerToys (Win+Ctrl+T), AutoHotkey,
or DeskPins installed or running.
- Not sleep/resume. Only one sleep/wake cycle occurred across the 08-26 and
09-01 occurrences (slept 08-30 10:31, woke 08-31 08:51). One predates it by
four days; the other postdates the wake by over a day.
- Not display reconfiguration. No display/monitor/PnP events in the System
event log spanning either occurrence.
- Not version-specific. Carried across
1.37937.1->1.40609.1. - Not the screenshot/content-protection helper. That routine snapshots
isAlwaysOnTop() per window and restores in a finally, re-applying only where
already true. It can escalate an existing pin to screen-saver level but cannot
originate one.
Machine-class difference
I see this on a laptop and never on my desktop. That matches @AITRUPEACE's report
in #87895 ("on my desktop its not an issue, on my laptop it is an issue, both
1.40609.0") — same split, same build family. Two independent reports of a
machine-class difference on the same version seems worth triaging on. I could not
correlate it with power state, display count, or display scaling.
Suggested fixes
- Log
setAlwaysOnTopcalls (target window id + level + call site). One line
would make this diagnosable from a single occurrence. Right now main.log
records nothing when a window's topmost state changes, which is the sole reason
this has taken weeks of guesswork across many duplicate reports.
- Guard the main window. If always-on-top is only ever intended for overlay
and popout windows, assert or refuse when the target is the main window.
- Provide an escape hatch. Even a hidden setting to force the main window
non-topmost would let affected users recover without restarting.
Workaround for anyone landing here
Clears it immediately, no restart, no side effects:
powershell -Command "Add-Type -MemberDefinition '[DllImport(\"user32.dll\")] public static extern bool SetWindowPos(IntPtr h, IntPtr a, int x, int y, int cx, int cy, uint f);' -Name W -Namespace N; foreach($p in Get-Process claude){ if($p.MainWindowHandle -ne 0){ [N.W]::SetWindowPos($p.MainWindowHandle, [IntPtr](-2), 0,0,0,0, 0x0013) } }"This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗