Agent-opened Terminal tabs never report ready on Windows: shell integration script is recreated at spawn time

Status Open
Reported on v2.1.286
Maintainer reply None cached
Activity 3 comments · opened Oct 2, 2026

Environment

  • Claude Desktop 2.19675.0.0 (x64), installed from the Microsoft Store (MSIX)
  • Windows 11 Home Single Language 26200
  • Windows PowerShell 5.1 (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe)
  • PowerShell 7 (pwsh) is not installed on this machine
  • Claude Code 2.1.286 (the copy bundled with the desktop app)

What happens

The Terminal panel works when the user opens a tab by hand — the shell runs
and accepts input normally. What fails is a tab opened programmatically (by the
agent, via the terminal tool): it is reported as never reaching a prompt and is
closed. The tab prints

AVISO: Claude terminal integration did not load: Exceção ao chamar "ReadAllText"
com "1" argumento(s): "Não foi possível localizar uma parte do caminho
'C:\Users\<user>\AppData\Roaming\Claude\terminal-shell-integration\pwsh\claude-desktop.ps1'."
PS E:\...>

and is then closed after 60 s with "had not reached a prompt after 60 s".

Note that the PowerShell prompt is on screen. What never arrives are the
shell-integration markers the app waits on, so the readiness check times out
even though the shell is perfectly usable.

The practical effect: the agent cannot hand the user an interactive prompt. In
our case a fiscal CLI requires a typed confirmation before an irreversible
action (transmitting an invoice to the tax authority), and that confirmation
cannot be routed to the user from inside the session. The user can still run
the command himself in a tab he opens.

Why it looks like a race, not a missing file

  1. The file exists and is complete: 6875 bytes, and

[IO.File]::ReadAllText(...) on that exact path succeeds from a normal
PowerShell session.

  1. Its mtime moves to the moment a tab is opened. Observed directly: the

file was stamped 09:07:08; opening a test tab at 10:19 changed the stamp to
10:19. The app rewrites it on every spawn.

  1. The exception is DirectoryNotFoundException ("could not find a **part of

the path**"), not FileNotFoundException. A directory component was
missing at read time — consistent with the pwsh\ folder being removed and
recreated while the shell is starting.

So the reader appears to run against the tree while the writer is still
replacing it.

Not a user-side misconfiguration

The ReadAllText call is not in the user's profile: neither
Documents\WindowsPowerShell\profile.ps1 nor
Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 mentions
terminal-shell-integration. The app injects it when spawning the tab.

Possibly relevant: the script lives under a folder named pwsh\, but the shell
being launched is Windows PowerShell 5.1 (powershell.exe); pwsh is not
installed here. If the writer and the reader disagree about which shell folder
to use, that would produce the same symptom.

Reproduction

  1. Windows 11, Claude Desktop from the Microsoft Store, Windows PowerShell 5.1,

no pwsh installed.

  1. Have the agent open a Terminal tab (any command).
  2. Observe the warning above, the readiness timeout, and the tab closing after

60 s — while the prompt itself is visible on screen.

Opening a tab manually in the same panel works, which is why this reads as a
readiness-detection failure rather than a shell failure.

Reproduced on first use and again more than an hour later, with the app running
the whole time — so it is not a cold-start or first-run condition.

Suggested fix

Write the integration script before spawning the shell and do not delete
the directory on each spawn (write to a temp file and rename, or just overwrite
in place). Failing the read should also not be fatal to readiness detection —
falling back to a timeout-free heuristic would keep the panel usable.

View original on GitHub ↗

3 Comments

AlexisDx13 · 12 days ago

I'm hitting the same symptom (same app build, 2.19675.0.0, MSIX from the Store; Windows 11 Home 26300), but with PowerShell 7.6.6 — so it's not tied to Windows PowerShell 5.1 or to the pwsh\ folder name. The evidence here points to MSIX file-system virtualization rather than a race.

The script only exists inside the package's virtualized AppData:

  • From a process inside the app's package context (e.g. the agent's own shell tool), the file is visible at the regular path and at the package's private copy, identical (same size, same mtime, rewritten on every tab spawn):

``
%APPDATA%\Claude\terminal-shell-integration\pwsh\claude-desktop.ps1 6875 bytes 18:48
%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\terminal-shell-integration\pwsh\claude-desktop.ps1 6875 bytes 18:48
``

  • From a Terminal panel tab (a shell spawned by the app itself for the panel; the command was sent with the code block's Run button), it does not exist:

``
PS> Test-Path C:\Users\<user>\AppData\Roaming\Claude\terminal-shell-integration\pwsh\claude-desktop.ps1
False
``

So the app's writes to %APPDATA% are redirected to LocalCache (standard MSIX behaviour), but the shells it spawns for the Terminal panel run with the real file-system view and can't see them. This also explains both observations in the original report: the file "exists and reads fine" and its mtime moves on each spawn, as long as you check from inside the package, which is the view the app itself has.

Implication for the fix: writing the script before spawning, or replacing it atomically, won't help, because the shell will never see it. The script needs to live somewhere the spawned shell can see, e.g. a path outside the virtualized AppData, or be passed to the shell directly (-Command / -EncodedCommand). Any agent-side check of that path will be misleading too, because it sees the virtualized view.

---

_Diagnosed and written up by Claude (Claude Code in the desktop app) while debugging this in my session; I ran the panel-side check and reviewed this before posting._

loris-gd · 12 days ago

Confirming the MSIX file-system virtualization diagnosis from the comment above, with two additions: the nonce file is affected too (so copying the script into the real %APPDATA% is not enough), and a junction workaround that fixed it here.

Environment

  • Claude Desktop 2.19675.0.0 x64, MSIX (Claude_pzs8sxrjxfjjc)
  • Windows 11 Pro 23H2 (build 22631)
  • Windows PowerShell 5.1.22621; pwsh not installed; no PowerShell profile files

Startup arguments and environment of an agent-opened tab

The shell is started by Claude.exe as:

powershell.exe -NoProfile -NoExit -Command "try { ... . ([ScriptBlock]::Create([System.IO.File]::ReadAllText($env:CLAUDE_DESKTOP_TERMINAL_HOOKS))) } catch { ... }"

Its environment block, read from the process 39 ms after start (before the script clears the variables):

CLAUDE_DESKTOP_TERMINAL_HOOKS      = %APPDATA%\Claude\terminal-shell-integration\pwsh\claude-desktop.ps1
CLAUDE_DESKTOP_TERMINAL_NONCE_FILE = %APPDATA%\Claude\terminal-shell-integration\spawn\<16 hex chars>

What each side sees

| Check | Inside the package (agent's shell tool) | In the spawned tab shell |
|---|---|---|
| nonce file spawn\<id> | exists, 50 bytes | Test-Path → False; spawn\ is empty |
| nonce file deleted after startup? | no, still present minutes later | — |

Before this, I had copied terminal-shell-integration\ into the real %APPDATA% by hand. With that copy the hooks script loads, but the nonce file still isn't visible to the shell. claude-desktop.ps1 swallows the read error (try { ReadAllLines } catch {}), so the module installs with an empty nonce and display nonce. Checked inside the tab:

promptModule=[ClaudeDesktopTerminal] nonceSet=False dnSet=False psrl=[2.0.0] readlineFn=True

The OSC 133 marks then go out as nonce= (empty) and are ignored. The symptoms:

  • the tab stays blank until Ctrl+C;
  • run_in_terminal fails with "had not reached a prompt after 60 s";
  • the tab's is_running_command stays null;
  • stop_terminal_tab reports "could not tell whether anything is still running".

Workaround (verified)

From a PowerShell outside Claude Desktop (for example Windows Terminal from the Start menu). Claude's own tools see the virtualized view, so this cannot be done from inside the app:

$real = "$env:APPDATA\Claude\terminal-shell-integration"
Rename-Item $real "terminal-shell-integration.manual-copy"   # only if a real copy exists
New-Item -ItemType Junction -Path $real -Target "$env:LOCALAPPDATA\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\terminal-shell-integration"

After this, two consecutive run_in_terminal calls (echo ..., then Get-Location) were typed, completed and returned to a prompt, and the tabs report is_running_command: false. No restart needed.

To undo it: cmd /c rmdir "$env:APPDATA\Claude\terminal-shell-integration". This removes the link only, not the files it points to.

Suggested fix

Both files the app hands to the shell (hooks script and nonce) must be reachable from the shell's non-virtualized view. Options:

  • write them outside the virtualized %APPDATA% (for example a per-user temp dir that isn't redirected);
  • resolve the package's real path before putting it in the environment;
  • pass the nonce through the environment or the command line instead of a file.

The script should also surface a nonce read failure instead of silently continuing with an empty nonce. That silent fallback is what turns this into an undiagnosable 60 s timeout.

---

_Diagnosed by Claude (Claude Code in the desktop app) in my session; I ran the tab-side checks and the junction myself and reviewed this before posting._

oniszvka · 12 days ago

Same symptom here, with one difference that may matter for the fix: on this machine the junction workaround from the comment above makes things worse, because the app then skips the shell integration entirely.

Environment

  • Claude Desktop 2.19675.0.0 x64, MSIX (Claude_pzs8sxrjxfjjc)
  • Windows 11 Pro, build 26200
  • PowerShell 7.6.6 (pwsh), no PowerShell profile files

Observations

Command lines of the tab shells were read with Get-CimInstance Win32_Process right after the tab opened.

| State of %APPDATA%\Claude\terminal-shell-integration (real file system) | How the app starts the tab shell | Result |
|---|---|---|
| Missing (default) | pwsh.exe -NoProfile -NoExit -Command "try { ... ReadAllText($env:CLAUDE_DESKTOP_TERMINAL_HOOKS) ... }" | Warning "Claude terminal integration did not load ... Could not find a part of the path ...\pwsh\claude-desktop.ps1", run_in_terminal times out |
| Junction to %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\terminal-shell-integration | plain pwsh.exe, no arguments at all | No warning, the PowerShell banner shows, no marks, run_in_terminal times out |
| Real copy of the folder (no reparse point) | bootstrap is back | Hooks load without warning. The nonce file is created at ...\LocalCache\Roaming\Claude\terminal-shell-integration\spawn\<16 hex> (50 bytes, created the moment the tab opened) and is invisible to the shell, so the nonce stays empty and run_in_terminal times out |
| Real copy, but spawn\ replaced by a junction to the package's spawn\ | plain pwsh.exe again | Same as the full junction |

Removing the junction brought the bootstrap back each time, so on this build any reparse point inside the integration folder seems to make the app drop the integration. A guess, not verified: the app's write or existence check fails through the MSIX virtualization when the real path contains a reparse point.

So none of the file-level workarounds works here. That supports the suggested fix: put both the hooks script and the nonce somewhere the non-virtualized shell can read, or pass them on the command line or in the environment.

Possibly related: the Run button on a code block had no visible effect for a PowerShell command in the same session.

---

_Diagnosed with Claude (Claude Code in the desktop app) in my session. I ran the tab-side commands myself and reviewed this before posting._