Bundled Anthropic BYOK rejects stable vscode.lm User-only requests with empty system block
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
- Copilot Chat Extension Version: Built-in `github.copilot-chat` 0.63.0
- VS Code Version: 1.135.0 (`08d4889f9ec4a1685d257b9b95de036c8e1ce1e5`)
- OS Version: Arch Linux x64, Wayland (Hyprland)
- Feature: Third-party Language Model API consumer using the built-in Anthropic BYOK provider
- Selected model: `anthropic/claude-haiku-4-5-20251001` (the provider exposed 10 Anthropic models)
- Logs:
```text
request: LanguageModelChatMessage.User("Reply with exactly OK."), no tools, no history
roster: 10 anthropic model(s)
model: anthropic/claude-haiku-4-5-20251001
RESULT: FAILURE — 400 {"type":"error","error":{"type":"invalid_request_error","message":"system: text content blocks must be non-empty"},"request_id":"req_011CecqAgwTSd1VaLu2aLUUv"}
```
## Steps to reproduce
1. Start VS Code with no GitHub/Copilot authentication. In **Chat: Manage Language Models**, configure the built-in **Anthropic** provider with a workspace-scoped Anthropic Console API key.
2. From a third-party extension command, make the smallest stable `vscode.lm` request:
```js
const models = await vscode.lm.selectChatModels({ vendor: 'anthropic' });
const model = models.find(candidate => /haiku/i.test(candidate.id)) ?? models[0];
const response = await model.sendRequest(
[vscode.LanguageModelChatMessage.User('Reply with exactly OK.')],
{ justification: 'Minimal Anthropic BYOK provider reproduction.' },
new vscode.CancellationTokenSource().token,
);
for await (const text of response.text) {
console.log(text);
}
```
3. Observe that model discovery succeeds, but `sendRequest` fails upstream with HTTP 400 before producing a response.
This was reproduced from a copied profile whose `vscode.github-authentication` secret was removed. The UI showed **Sign In**. The credential was a workspace-scoped key, not an identity-linked key. As a control, the same key listed 10 models and the same Haiku model answered a direct Anthropic SDK request successfully.
## Expected
A non-empty stable-API User message should reach the selected Anthropic model and stream its answer. A missing system message should be omitted from the Anthropic request.
## Actual and owning layer
The third-party request contains one non-empty User text part, but the bundled provider adds an empty Anthropic system text block.
At the 1.135.0 product commit, [`apiMessageToAnthropicMessage`](https://github.com/microsoft/vscode/blob/08d4889f9ec4a1685d257b9b95de036c8e1ce1e5/extensions/copilot/src/extension/byok/common/anthropicMessageConverter.ts#L104-L148) initializes `{ type: 'text', text: '' }` and returns it even when all input messages have the stable User/Assistant roles. [`AnthropicLMProvider`](https://github.com/microsoft/vscode/blob/08d4889f9ec4a1685d257b9b95de036c8e1ce1e5/extensions/copilot/src/extension/byok/vscode-node/anthropicProvider.ts#L265-L276) then unconditionally sends `system: [system]`.
The same two shapes are still present on current `main` at `22f76d2e5fbf22b786986e2f7debf7446af7b4ad`.
This makes every stable third-party User/Assistant-only request hit the same invalid empty system block. A consumer cannot work around it by changing User text, and the System role remains a proposed API that Marketplace extensions cannot use.
One possible provider-side fix is to omit `system` when `system.text` is empty and it has no cache control, with a regression test for a single non-empty User message:
```ts
system: system.text || system.cache_control ? [system] : undefined,
```
Contributor guide
Assessment
This issue has not been assessed yet.