Agent-opened Terminal tabs never report ready on Windows: shell integration script is recreated at spawn time
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
- The file exists and is complete: 6875 bytes, and
[IO.File]::ReadAllText(...) on that exact path succeeds from a normal
PowerShell session.
- 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.
- 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: neitherDocuments\WindowsPowerShell\profile.ps1 norDocuments\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 mentionsterminal-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
- Windows 11, Claude Desktop from the Microsoft Store, Windows PowerShell 5.1,
no pwsh installed.
- Have the agent open a Terminal tab (any command).
- 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.
3 Comments
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 thepwsh\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:``
``%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
``
``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 toLocalCache(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._
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_pzs8sxrjxfjjc)pwshnot installed; no PowerShell profile filesStartup arguments and environment of an agent-opened tab
The shell is started by
Claude.exeas:Its environment block, read from the process 39 ms after start (before the script clears the variables):
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.ps1swallows the read error (try { ReadAllLines } catch {}), so the module installs with an empty nonce and display nonce. Checked inside the tab:The OSC 133 marks then go out as
nonce=(empty) and are ignored. The symptoms:run_in_terminalfails with "had not reached a prompt after 60 s";is_running_commandstaysnull;stop_terminal_tabreports "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:
After this, two consecutive
run_in_terminalcalls (echo ..., thenGet-Location) were typed, completed and returned to a prompt, and the tabs reportis_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:
%APPDATA%(for example a per-user temp dir that isn't redirected);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._
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_pzs8sxrjxfjjc)pwsh), no PowerShell profile filesObservations
Command lines of the tab shells were read with
Get-CimInstance Win32_Processright 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_terminaltimes out || Junction to
%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\terminal-shell-integration| plainpwsh.exe, no arguments at all | No warning, the PowerShell banner shows, no marks,run_in_terminaltimes 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 andrun_in_terminaltimes out || Real copy, but
spawn\replaced by a junction to the package'sspawn\| plainpwsh.exeagain | 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._