Claude BYOK broken in 1.136: SDK download deadlock for signed-in users
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
Does this issue occur when all extensions are disabled?: Yes
- VS Code Version: 1.136.0 (commit 520fb30b2d3d324b4cb2342f6e88e2cd93751de1, 2026-09-02T09:13:42Z)
- OS Version: Windows 11 Pro 10.0.26200.9278, x64
Steps to Reproduce:
1. On 1.135, sign in to GitHub with an account that has **no Copilot subscription**, and use the Claude agent host via native/BYOK (`ANTHROPIC_BASE_URL` + `ANTHROPIC_AUTH_TOKEN`), leaving `chat.agentHost.allowSignedOutWhenUsable` at its `false` default. Confirm it works and that `sdk-cache/claude//` exists.
2. Upgrade to 1.136, where `product.agentSdks.claude.version` moves to `0.3.239`.
3. Restart and open the chat model picker.
**Expected:** the SDK for the newly pinned version is downloaded, or a reachable prompt/command offers to download it.
**Actual:** Claude is greyed out with an "Upgrade" label, model count is 0, `agenthost.log` repeats "SDK not downloaded yet" indefinitely, and no download is ever attempted. There is no in-product way to recover.
---
## Summary
Claude agent is permanently unusable after upgrading to 1.136 when using native/BYOK while signed in to GitHub without a Copilot subscription: the required SDK version changed, but neither the automatic nor the manual download path can be reached.
## What happened
Claude worked fine in 1.135 via native/BYOK (`ANTHROPIC_BASE_URL` + `ANTHROPIC_AUTH_TOKEN`). I am signed in to GitHub, but I have no Copilot subscription, so the agent host's proxy models are unavailable to me and BYOK is the only path I use. After upgrading to 1.136 the Claude entry in the chat model picker is greyed out with an "Upgrade" label and cannot be selected.
`agenthost.log` repeats these lines 100+ times per session, and there is **no download attempt, no URL, and no network error anywhere in the log**:
```
[Claude] SDK not downloaded yet; deferring chat metadata until a session triggers the download
[Claude] SDK not downloaded yet; deferring chat discovery
[Claude] Auth token updated
[Claude] Models refreshed (merged). Count: 0,
```
Sign-in itself is healthy. In the same session the Copilot provider resolves the account and lists its (non-Claude) model:
```
[Copilot] Found 1 models: Auto
[Claude] Models refreshed (merged). Count: 0,
```
So this is not an authentication failure. The account simply yields no Claude proxy models without a subscription.
## Root cause
**1. The required SDK version changed, and the cache lookup is exact-match.**
1.136's `product.json` pins `agentSdks.claude.version` to `0.3.239`. My cache only had `0.3.220` from 1.135. `isSdkResolvableWithoutDownload` builds the path from the pinned version and only checks that exact directory:
```js
async isSdkResolvableWithoutDownload(e){
if(process.env[e.devOverrideEnvVar]) return !0;
let t=this._productService.agentSdks?.[e.id]; // → 0.3.239
if(!t) return !1;
let i=qE(e); if(!i) return !1;
let o=v.joinPath(v.file(this._cacheDir(e.id,t.version,i)),".complete");
return this._fileService.exists(o); // looks only at 0.3.239/win32-x64/.complete
}
```
The existing `0.3.220` directory is never consulted. That part is reasonable on its own — a download is genuinely needed.
**2. Neither download trigger is reachable in this configuration.**
There are exactly two ways `loadSdkRoot` gets called:
- `ensureAvailable()`, when a session actually starts — but the entry is greyed out, so no session can start.
- The `workbench.action.chat.agentHost.downloadAgentSdk` command, invoked by the "Download the Claude Agent" notification button.
That notification is gated by `Fsr`:
```js
function Fsr(s){
if(!(!s.allowSignedOutWhenUsable || !s.entitlementResolved || s.signedIn)){
if(s.hasModels) return "resolved";
switch(s.download){
case "downloading": return;
case "notDownloaded": return s.downloadRequested ? void 0 : "downloadOffered";
case "ready": return "noAccount";
}
}
}
```
The outer guard requires `!s.signedIn`. Because I *am* signed in, it short-circuits regardless of any setting. Evaluating it over both inputs, with `entitlementResolved: true`, `hasModels: false`, `download: "notDownloaded"`:
| `signedIn` | `allowSignedOutWhenUsable` | result |
|---|---|---|
| false | false | `undefined` |
| false | true | `downloadOffered` |
| true | false | `undefined` |
| true | true | `undefined` |
So for a signed-in user there is **no configuration at all** that surfaces the prompt — flipping `chat.agentHost.allowSignedOutWhenUsable` to `true` changes nothing (I tried; it did not help). The one code path that offers the download is reachable only by signed-*out* users who have also opted into an experimental setting.
`downloadAgentSdk` is registered without `f1:!0`, so it is not in the Command Palette either.
Result: a deadlock. The SDK will not download because no session can start, and no session can start because the SDK is missing.
**3. Why BYOK is affected at all.**
`_refreshModels` merges two sources, and both come up empty here:
```js
let t = await this._sdkService.canLoadWithoutDownload();
let [i, o] = await Promise.allSettled([
e ? this._fetchProxyModels(e) : Promise.resolve([]), // no subscription → no Claude models
t ? this._fetchNativeModels() : Promise.resolve([]) // t === false → []
]);
```
`_fetchNativeModels` needs the SDK loaded before it can call `query()` / `supportedModels()`, so the BYOK path — which needs no subscription by design — is transitively blocked by an SDK download whose only user-facing trigger is gated on sign-in state. With both sources empty the merged count is 0 and the entry renders as greyed-out with "Upgrade", which misattributes an SDK-cache problem to entitlement.
This is why the upgrade surfaced it: in 1.135 the pinned version was already cached, so the download branch was never exercised.
## Not a network problem
The CDN is reachable and the artifact is intact. Downloading it by hand and placing it in the cache:
```
https://main.vscode-cdn.net/agent-sdk/claude/0.3.239/win32-x64.tgz
→ HTTP 200, 110 MB
```
`claude.exe` sha256 and size match `manifest.json` exactly (`0bc1304c…20a2b`, 337672352 bytes), and `sdk.mjs` imports cleanly.
After extracting to `%APPDATA%/Code/agent-host/sdk-cache/claude/0.3.239/win32-x64/`, writing an empty `.complete` marker, and restarting, **Claude works normally again over BYOK** — same account, same sign-in state, no setting changed:
```
[Claude] Models refreshed (merged). Count: 4, Opus (1M context), Sonnet, Sonnet 5 (1M context), Haiku
[Claude] Native account check: setUp=true, provider=firstParty, tokenSource=ANTHROPIC_AUTH_TOKEN
```
That confirms the missing download was the only thing wrong.
## Suggested fixes
Any one of these would break the deadlock:
- Don't gate `downloadOffered` on sign-in state or `allowSignedOutWhenUsable`. A missing SDK is orthogonal to authentication — the prompt should appear whenever `download === "notDownloaded"` and an agent is enabled, for signed-in and signed-out users alike.
- Register `workbench.action.chat.agentHost.downloadAgentSdk` with `f1: true` so there is a manual escape hatch via the Command Palette.
- When BYOK is enabled (`chat.agentHost.byokModels.enabled`), call `ensureAvailable()` eagerly during model refresh instead of deferring to a session that cannot start.
- On a pinned-version change, surface it as an actionable "update required" state rather than silently logging "SDK not downloaded yet" on a loop. Also avoid labelling a missing-SDK state as "Upgrade", which points users at a subscription that would not fix it.
## Possibly related
- #330475 — first message fails when the SDK downloads lazily during the first turn
- #324229 — request for agent-host binary download progress notifications
- #323049 — improving discoverability of native (BYOK) Claude in the agent host
Contributor guide
Assessment
This issue has not been assessed yet.