Skip to content

Claude V2 adapter passes bare "claude" to the Agent SDK on Windows, so every turn fails with "Provider turn failed." #15011

Closed

Description

@MundaysDesign

What happened

After updating the desktop app to a nightly build, every Claude turn fails with "Provider turn failed." Codex turns on the same install keep working.

Diagnosis

On Windows, ClaudeAdapterV2 passes the configured binaryPath to the Claude Agent SDK as pathToClaudeCodeExecutable unchanged:

  • apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts:871 (tag v0.0.46-nightly.20261003.2610; :875 on main): pathToClaudeCodeExecutable: input.settings.binaryPath
  • ClaudeAdapterV2.ts:7664 sets binaryPath to expandHomePath(config.binaryPath), which is "claude" with the default settings.

The SDK spawns that value without a shell and without PATH/PATHEXT resolution, so a bare claude cannot be found on Windows. resolveClaudeSdkExecutablePath in apps/server/src/provider/Drivers/ClaudeExecutable.ts already handles this case (added in #3740 for v1). It resolves claude through PATH and follows the npm claude.cmd shim to node_modules/@anthropic-ai/claude-code/bin/claude.exe. That helper is called only from provider/Layers/ClaudeProvider.ts (the health probe) and ClaudeAdapterV2.testkit.ts. The V2 adapter, which runs real turns, never calls it.

Result: the SDK query exits immediately. finalizeActiveTurnAfterQueryExit records a transport_error with the default message "Provider turn failed.", and the underlying spawn error only reaches the orchestration-v2.claude-query-stream-failed warning log. The desktop app does not persist that log. The provider status check still reports Claude as healthy, because the probe path does resolve the executable.

This is a regression for every Windows user with an npm-installed Claude Code and the default binaryPath, starting when their threads move to orchestration v2.

Suggested fix: resolve config.binaryPath with resolveClaudeSdkExecutablePath in the V2 adapter layer (around ClaudeAdapterV2.ts:7664) before handing it to makeClaudeAdapterV2. Also consider surfacing the squashed cause in the failure message; #14969 may cover that part.

Steps to reproduce

  1. On Windows, install Claude Code via npm (npm i -g @anthropic-ai/claude-code), so claude on PATH is the %APPDATA%\npm\claude.cmd shim.
  2. Leave the Claude provider's binary path at the default claude.
  3. Install desktop nightly 0.0.46-nightly.20261003.2610 and send a message in a new or existing Claude thread.
  4. The turn fails about 1.4 s later with "Provider turn failed." No incoming SDK events are logged after query.open and prompt.offer.

Spawn check outside T3 (Node 24, no shell), same machine:

  • spawn('claude', ['--version']) → ENOENT
  • spawn('<npm>\\node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe', ['--version']) → 2.1.288 (Claude Code)

Version

0.0.46-nightly.20261003.2610 (desktop, Nightly channel; commit 8ed276c)

Environment

Windows 10 Pro 19045 x64, Node 24.11.1, Claude Code 2.1.288 (npm global install), desktop app with local server

Evidence

# provider event log, every post-upgrade Claude turn (outgoing only, nothing incoming):
[2026-10-03T03:44:54.298Z] NTIVE: {"provider":"claudeAgent","protocol":"claude-agent-sdk.query", ... "payload":{"type":"query.open","options":{"model":"claude-opus-5-5[1m]", ... "pathToClaudeCodeExecutable":"claude", ...}}}
[2026-10-03T03:44:54.298Z] NTIVE: { ... "payload":{"type":"prompt.offer", ... }}

# statev2.sqlite, orchestration_v2_projection_turn_items (same turn):
"type":"error","title":"Provider error","failure":{"class":"transport_error","message":"Provider turn failed.","code":null,"retryable":null}
# provider turn started 03:44:54.286Z, failed 03:44:54.299Z

# orchestration_v2_projection_runs since upgrade: 5 x claudeAgent "failed" (old and brand-new threads), codex "running" normally

Related issues

#3740 fixed the same problem for the v1 Claude path, and the V2 adapter doesn't use that fix. #13471 made V2 expand ~ in the binary path but did not add PATH/shim resolution. #14969 (open) shows provider failure reasons but would not fix the spawn. I found no existing issue for this.

Fix applied or workaround

Set the Claude provider's binary path (Settings → Providers → Claude, i.e. providerInstances.claudeAgent.config.binaryPath in settings.json) to the absolute path %APPDATA%\npm\node_modules\@anthropic-ai\claude-code\bin\claude.exe. Confirmed: a new Claude thread then launches with that path and the turn runs normally.

Note: the server picked up the settings edit, but an existing thread whose Claude provider session was opened before the edit still launched with "claude" and failed again on retry. Existing provider sessions seem to keep the binaryPath they were created with, so affected users may need to restart the app (or start new threads) after applying the workaround.

Filed by

Claude Code (claude-opus-5-5) via t3 triage

Activity

  1. juliusmarminge commented on Oct 3, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Triage

    Thanks for the precise diagnosis, @MundaysDesign. The spawn check outside T3 made this easy to confirm. It's still broken on main (43bd667), because the only commit after nightly 8ed276c doesn't touch this path.

    What's happening: createClaudeAdapterV2 only runs expandHomePath on binaryPath (around line 7671 of ClaudeAdapterV2.ts), then passes that string straight through as pathToClaudeCodeExecutable (around line 875). The default is the bare command claude. The Agent SDK spawns that value without a shell and without the Windows PATH/PATHEXT lookup, so an npm claude.cmd shim never starts. The turn then ends as a transport_error with "Provider turn failed.", and the actual spawn error only shows up in the orchestration-v2.claude-query-stream-failed warning.

    Why Claude still looks healthy: resolveClaudeSdkExecutablePath already handles this case. It resolves claude through PATH, then finds bin/claude.exe or cli.js next to the shim. But real turns never call it. Only the capabilities probe in ClaudeProvider.ts and the V2 recording testkit use it. The CLI status check uses resolveSpawnCommand, which can use a shell, so Claude reports as healthy while every V2 turn fails. Codex doesn't go through this SDK option, so it isn't affected.

    Not a duplicate: #3740 (merged) wired that helper into the old V1 Claude session path. #13471 (merged) only expands ~ for V2. The open PR #14969 improves the failure text when a session fails to open, but it doesn't resolve the executable or touch this query-exit path.

    Fix direction: in createClaudeAdapterV2, resolve config.binaryPath with resolveClaudeSdkExecutablePath before calling makeClaudeAdapterV2. Then add a case for a bare claude on Windows to the existing executable-path test.

    Workaround: setting the absolute path you described works. A session that's already open keeps the runtime it was created with (ProviderSessionManager reuses existing.exposedRuntime), so after changing the binary path, start a new thread or restart the app.

  2. added
    bugSomething is broken or behaving incorrectly.
    on Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions