Windows: main window intermittently gains WS_EX_TOPMOST (breaks Alt+Tab) — captured correlation with LocalSessions.setFocusedSession

Status Open
Maintainer reply None cached
Activity 3 comments · opened Sep 5, 2026

Summary

On Windows, the Claude desktop app's main window intermittently acquires the
WS_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 with
SetWindowPos(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

  1. Log setAlwaysOnTop calls (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.

  1. 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.

  1. 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) } }"

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗