Assistant message "refusal": null in outbound chat-completion request breaks strict OpenAI-compatible providers (e.g. Gemini)
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 55/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- node.js, typescript
- Domain
- api
Research direction
Start with the Copilot SDK/CLI outbound message serialization and reproduce the issue using the minimal TypeScript session against the Gemini-compatible endpoint. Inspect the captured request in ~/.copilot/logs/process-*.log, then verify that the assistant message no longer contains a null refusal field and that the second request completes successfully after a tool call.
Written by the indexing model from the issue text.
Description
TL;DR
@github/copilot-sdk@1.0.8 (bundled @github/copilot@1.0.73) sends an assistant message containing a literal JSON "refusal": null field in the outbound messages[] array when using a BYOK OpenAI-compatible provider. OpenAI's own API accepts null here, so nobody notices. But Google Gemini's OpenAI-compatible endpoint (generativelanguage.googleapis.com/v1beta/openai) strictly rejects it with 400 Bad Request ("Value is not a string: null"), and this error body is again swallowed by the CLI's retry logic, surfacing only CAPIError: 400 400 400 Bad Request with no indication of the real cause.
This is the same failure class as #1129 (copilot_mcp_server_name leaking into tools[]), but a different field/message, and #1129's fix does not cover this case — confirmed still failing on the latest published versions (SDK 1.0.8, CLI 1.0.73) as of 2026-07-21.
Environment
@github/copilot-sdk:1.0.8(latest stable on npm at time of testing)@github/copilot(bundled darwin-arm64 binary):1.0.73(latest stable)- Node.js:
v24.16.0 - OS: macOS 15.7.7 (Darwin 24G720)
- Provider: BYOK,
type: "openai",baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai/" - Model:
gemini-2.5-flash - MCP server: local stdio MCP server (Atlassian Jira MCP), at least one tool call in the conversation
Reproduction
Minimal SDK-driven session with any BYOK OpenAI-compatible provider that does strict schema validation, once the conversation reaches a second turn after a tool call has been executed (i.e. once an assistant message with tool_calls needs to be replayed back to the model):
import { CopilotClient, approveAll } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({
onPermissionRequest: approveAll,
model: "gemini-2.5-flash",
provider: {
type: "openai",
baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai/",
apiKey: process.env.GEMINI_API_KEY!,
}
});
await session.sendAndWait({ prompt: "Please call a tool, then respond." });
Expected
A normal multi-turn chat: tool call executes, result is sent back, model produces a final answer.
Actual
The first request (before any tool call) succeeds. The second request (after the tool result is appended to history) fails:
CAPIError: 400 400 400 Bad Request
Root cause
Captured the outbound request body via debug logging (~/.copilot/logs/process-*.log, logLevel: 'debug'). The second request's messages[] array contains an assistant message shaped like:
{
"role": "assistant",
"content": "...",
"refusal": null, // ← non-standard-for-Gemini null value
"tool_calls": [ ... ]
}
Replaying the exact captured request body directly against Gemini via curl reproduces the failure:
{
"error": {
"code": 400,
"message": "Value is not a string: null",
"status": "INVALID_ARGUMENT"
}
}
Removing only the "refusal": null key from that same assistant message and re-sending the identical request returns 200 OK with a valid completion. So the non-standard null value for refusal is the sole cause.
Verified by diffing:
- Captured request as-is (with
"refusal": null) →400 - Same request with
refusalkey removed →200
Related — second, distinct bug with gemini-3.1-pro-preview
Using model gemini-3.1-pro-preview instead fails on the same kind of second-turn request with a different Gemini-side error:
{
"error": {
"code": 400,
"message": "Function call is missing a thought_signature in functionCall parts. This is required for tools to work correctly...",
"status": "INVALID_ARGUMENT"
}
}
Gemini 3.x requires the thought_signature value (returned by Gemini in extra_content.google.thought_signature on the assistant's tool_calls[].function) to be echoed back verbatim on the next request. The CLI does appear to attempt round-tripping this value (it is present in the outbound extra_content.google.thought_signature of the replayed tool_calls entry), but Gemini still rejects it — suggests either a serialization mismatch (e.g. wrapping/escaping) or the signature isn't being preserved correctly end-to-end. Not fully root-caused to the same file/line level as the refusal: null issue above, but flagging it here since it's the same "strict-provider vs. Copilot's OpenAI-compat serializer" problem class, on the same code path.
Workaround
None found yet on the client side
Related issues
- #1129 — same failure class (unknown/non-conformant field breaking strict OpenAI-compatible providers), different field, confirmed fixed for that specific field but not for this one.
- Dominant language
- Java
- Stars
- 10.5k
- Forks
- 1.5k
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 133
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from github/copilot-sdk
-
agentic-workflows
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
github/copilot-sdk#2709 · 1 comment ·
-
Difficulty 1/5 Under an hour Newbie friendliness 78/100
github/copilot-sdk#2673 ·
-
bug testing
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
github/copilot-sdk#2628 ·
-
agentic-workflows
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
github/copilot-sdk#2627 · 1 comment ·
-
agentic-workflows
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
github/copilot-sdk#2493 ·
All issues in github/copilot-sdk
Similar issues
-
bug
Difficulty 1/5 Under an hour Newbie friendliness 90/100
apache/cloudstack#14222 ·
-
[BUG]茶杯方块在取茶时会引发崩溃 Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
1.0.0-alpha2 Type/Improvement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
wso2/dpdp-accelerator#272 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
apache/rocketmq-dashboard#4860 · 1 comment ·
-
agent-audit bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Vault-Web/cloud-page#144 ·