Assistant message "refusal": null in outbound chat-completion request breaks strict OpenAI-compatible providers (e.g. Gemini)
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 3/5
- Tempo stimato
- 1-2 giorni
- Idoneità per principianti
- 55/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- node.js, typescript
- Ambito
- api
Direzione di ricerca
Inizia con la serializzazione dei messaggi in uscita di Copilot SDK/CLI e riproduci il problema usando la sessione TypeScript minima verso l’endpoint compatibile con Gemini. Esamina la richiesta acquisita in ~/.copilot/logs/process-*.log, quindi verifica che l’assistant message non contenga più un null refusal field e che la seconda richiesta venga completata correttamente dopo un tool call.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
TL;DR
@github/[email protected] (bundled @github/[email protected]) 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.
- Lingua principale
- TypeScript
- Stelle
- 10.5k
- Fork
- 1.5k
- Merge medio
- 1g 7h
- PR unite (30g)
- 98
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di github/copilot-sdk
-
documentation
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 92/100
github/copilot-sdk#2804 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
github/copilot-sdk#2798 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
github/copilot-sdk#2793 ·
I maintainer di solito rispondono entro 1 giorno
-
agentic-workflows
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
github/copilot-sdk#2782 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
github/copilot-sdk#2781 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di github/copilot-sdk
Issue simili
-
bug HemiStake
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
hemilabs/ui-monorepo#2413 ·
I maintainer di solito rispondono entro 1 giorno
-
component/ui framework/react kind/bug language/javascript
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
meshery/meshery#22216 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
type/bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
company delete fails with 500 on any company that has activity (cost events, inbox dismissals)Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
paperclipai/paperclip#14982 ·
I maintainer di solito rispondono entro 1 giorno
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 95/100
lingdojo/kana-dojo#31515 · 1 commento · 5 reazioni ·
I maintainer di solito rispondono entro 1 giorno