Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Assistant message "refusal": null in outbound chat-completion request breaks strict OpenAI-compatible providers (e.g. Gemini)

Aperta
#2,061 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

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:

  1. Captured request as-is (with "refusal": null) → 400
  2. Same request with refusal key 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

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di github/copilot-sdk

Tutte le issue di github/copilot-sdk

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.