Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Abierto
#2,061 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
3/5
Tiempo estimado
1-2 días
Aptitud para principiantes
55/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Tranquilo
Stack tecnológico
node.js, typescript
Área
api

Línea de trabajo

Comienza con la serialización de mensajes salientes de Copilot SDK/CLI y reproduce el problema usando la sesión TypeScript mínima contra el endpoint compatible con Gemini. Inspecciona la solicitud capturada en ~/.copilot/logs/process-*.log y verifica después que el assistant message ya no contenga un null refusal field y que la segunda solicitud se complete correctamente después de un tool call.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.
Lenguaje dominante
TypeScript
Estrellas
10.5k
Forks
1.5k
Merge medio
1 d 11 h
PR fusionados (30 d)
81

Preparar el entorno

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de github/copilot-sdk

Todos los issues de github/copilot-sdk

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.