Saltar al contenido principal

Corrige API 400 por rol no admitido sin romper las instrucciones del sistema

L
9 min de lecturaSolución de problemas API

Este error es un conflicto de contrato de roles. Usa param, payload final, mapping por endpoint y same-route smoke test para repararlo sin perder prioridad.

Corrige API 400 por rol no admitido sin romper las instrucciones del sistema

Si la API devuelve 400 con role 'system' is not supported on this model, no empieces rotando la clave, subiendo cuota o añadiendo reintentos. La solicitud llegó a una ruta que valida la estructura del mensaje, y esa ruta rechazó el valor final de messages[n].role. Es un choque de contrato de roles, no un problema genérico de autenticación o red.

El primer paso es leer el param del cuerpo de error y compararlo con el JSON final que salió después del SDK, framework, middleware, deployment de Azure o gateway compatible. La reparación debe conservar la intención de la instrucción del sistema y moverla a la superficie de instrucciones de mayor prioridad que acepte esa misma ruta.

Pista de falloDueño probablePrimer movimiento seguroVerificación
messages[n].role = systemEndpoint, gateway, Azure deployment o adapter contractConservar la instrucción y mapearla a la instruction surface admitida por esa rutaUna solicitud mínima en la misma ruta ya no falla por role
Se rechaza developerAPI version, compatibility layer o framework routeConfirmar si esa ruta de Chat Completions acepta el rol; si no, usar Responses instructions o un mapping específicoLa misma forma de payload pasa en la ruta seleccionada
Falla tool o function legacyProtocolo de tools incompatibleUsar el tool message y call id que documenta la rutaEl tool result se acepta sin role error
El rol no está en tu códigoAgent framework, prompt wrapper, middleware o SDK adapterRegistrar el outbound JSON final y desactivar o remapear el mensaje inyectadoEl repro mínimo y el flujo completo usan el mismo role set aceptado

Detente aquí antes de cambiar de proveedor, aumentar timeouts o tocar la cuota. Nada de eso corrige un contrato de roles inválido. La prueba real es una solicitud mínima contra el mismo provider, base URL, endpoint, API version, model o deployment, SDK adapter y framework route.

Lee el cuerpo de error antes de tocar el código

La parte útil de este 400 normalmente no es la frase en inglés, sino el objeto estructurado. Guarda status, type, code, message y, sobre todo, param. Si apunta a messages[0].role, mira el primer objeto del array. Si apunta a otro índice, revisa mensajes añadidos por history, tools, adapters o middleware.

No basta con mirar el array que escribiste en tu handler. Antes del envío, un memory prompt, default system prompt, guardrail middleware, retry wrapper, adapter OpenAI-compatible o gateway puede transformar el payload. Registra la forma final, desinfectada, lo más cerca posible del HTTP boundary.

{
  "endpoint": "POST /v1/chat/completions",
  "model": "example-model",
  "messages": [
    { "role": "system", "content": "[redacted instruction]" },
    { "role": "user", "content": "[redacted user task]" }
  ]
}

Ese log solo debe probar estructura. No registres API keys, bearer tokens, cookies, prompts privados, texto de usuarios, contenido de archivos ni salidas de tools. Lo que necesitas es endpoint, model o deployment, índice del mensaje, rol y capa que construyó el payload final.

Mapa para localizar el dueño del contrato de roles detrás de un unsupported role

Repara por endpoint, no por costumbre

La trampa principal es creer que hay una sustitución universal. system no siempre se reemplaza por developer, y mover instrucciones a user no es una reparación neutral. El soporte de roles depende del endpoint, API version, Azure deployment, provider gateway, SDK y a veces del framework de agentes.

Al 21 de mayo de 2026, OpenAI Responses tiene una superficie superior instructions para instrucciones de desarrollador. Las rutas oficiales actuales de Chat Completions usan messages y pueden aceptar roles de tipo instrucción en esas rutas. Eso no prueba que un gateway compatible, un deployment de Azure, un adapter antiguo o un alias de modelo de otro proveedor acepte el mismo contrato.

Si estás en una ruta oficial de Chat Completions que acepta developer, el cambio puede ser:

{
  "model": "your-selected-model",
  "messages": [
    { "role": "developer", "content": "Follow the support triage rules." },
    { "role": "user", "content": "Classify this API error." }
  ]
}

Si estás en Responses, conserva la instrucción persistente en instructions y envía la tarea del usuario por input:

{
  "model": "your-selected-model",
  "instructions": "Follow the support triage rules.",
  "input": "Classify this API error."
}

Si un gateway solo acepta user y assistant, no metas inmediatamente las instrucciones del sistema dentro de un user message. Busca primero un instruction field, model option, adapter setting o ruta alternativa. Si no existe, documenta que el fallback cambia la prioridad de instrucciones.

Antes y después de reparar el payload sin perder prioridad de instrucciones

Cada rol tiene una rama de reparación

Cuando se rechaza system, el objetivo es conservar la fuerza de la instrucción. En Chat Completions oficial puede ser developer; en Responses suele ser instructions; en un gateway puede ser un campo propio. Bajar a un user message es último recurso y debe tratarse como cambio de comportamiento.

Cuando se rechaza developer, a menudo estás en una capa compatible más antigua, un proveedor que replica un schema anterior, o una combinación Azure/API-version que no lo adoptó. Verifica base URL, endpoint y version antes de cambiar roles. Dos llamadas pueden verse iguales en el cliente y aplicar contratos distintos en el servidor.

Cuando se rechaza tool, inspecciona todo el protocolo de tools. Un tool result moderno suele necesitar tool call id y una forma de mensaje compatible con la API elegida. El antiguo function es otra rama. Mezclar function legacy con tool-call moderno puede producir role error aunque el modelo soporte tools.

Si el rol es null, falta, tiene mayúsculas incorrectas o se serializa como enum no reconocido, es un bug de construcción del cliente. Valida antes de enviar:

const allowedRoles = new Set(["developer", "user", "assistant", "tool"]);
for (const [index, message] of messages.entries()) {
  if (!allowedRoles.has(message.role)) {
    throw new Error("Unsupported outbound role at messages[" + index + "].role");
  }
}

La validación local no reemplaza la documentación del proveedor, pero evita que requests malformadas lleguen a producción.

El framework puede inyectar el rol que falla

Muchos equipos dicen “yo nunca envié system”. Puede ser cierto en la capa de aplicación y falso en el HTTP final. Agent frameworks, librerías de chat UI, wrappers RAG, tracing middleware y provider adapters pueden construir el array final después de tu código.

CapaQué inspeccionarPatrón de fallo
Agent frameworkDefault system prompt, memory prompt, tool policy, guardrail messageAparece un system o developer oculto antes del input
SDK adapterConversión OpenAI-compatible, enum mapping, soporte legacy de toolsLos nombres de rol se remapean o serializan distinto
GatewayProvider mode, upstream route, model aliasEl gateway acepta, pero upstream rechaza el contrato
Azure routeDeployment name, api-version, region, model familyEl mismo payload funciona en un deployment y falla en otro
MiddlewareRetry wrapper, prompt decorator, request loggerUn wrapper agrega instruction message en cada llamada

El punto correcto de observación es el límite más cercano al HTTP request. Si solo ves el objeto previo al framework, todavía no estás viendo el artifact que validó el servidor. El JSON final desinfectado te da índice, rol, endpoint y adapter path.

Verifica con una prueba mínima en la misma ruta

Que funcione en otra ruta no prueba que la ruta fallida esté arreglada. Misma ruta significa mismo provider, base URL, endpoint, API version, model o deployment alias, SDK adapter, framework settings y tool protocol.

Usa el payload más pequeño que todavía ejercite la rama reparada:

curl "$BASE_URL/v1/chat/completions" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "your-selected-model",
    "messages": [
      {"role": "developer", "content": "Answer in one sentence."},
      {"role": "user", "content": "Say ready."}
    ]
  }'

Cambia una sola variable por vez. Si pasas de Azure a OpenAI directo, cambias API version, renombras el modelo, quitas el framework y acortas el prompt al mismo tiempo, el resultado ya no diagnostica la ruta original.

Después de que la prueba mínima pase, ejecuta el flujo completo con role logging activo. Así detectas frameworks que reintroducen system, developer, tool o function después de que el ejemplo mínimo ya funcionaba.

Escala con evidencia, no con una captura

Si el repro mínimo en la misma ruta sigue fallando, prepara un paquete para el proveedor, el equipo de Azure, el operador del gateway o el maintainer del framework. Una captura de pantalla no basta; suele faltar route, version, adapter y request id.

Incluye:

  • request JSON desinfectado, sin secretos ni datos de usuario
  • base URL y path exactos, o nombre de provider route
  • model, deployment, region y API version
  • versiones de SDK, framework, adapter y gateway
  • response body, status code, param y code
  • request ID, correlation ID o gateway trace ID
  • minimal same-route repro y una reparación controlada que intentaste

Checklist de evidence packet para escalar un unsupported role

No incluyas API keys, bearer tokens, prompts privados, texto de clientes, contenido de archivos, salidas de tools ni capturas con secretos. Si el proveedor confirma que la ruta no acepta el rol necesario, las opciones honestas son cambiar a un endpoint con instruction surface adecuada, configurar el adapter para preservar prioridad, o documentar que el fallback cambia comportamiento.

Checklist de recuperación rápida

  1. Captura response body y el param fallido.
  2. Loguea el outbound JSON final después de framework y adapters.
  3. Identifica el dueño: direct OpenAI, Azure, provider gateway, SDK o framework.
  4. Mueve la instrucción al canal de mayor prioridad que soporte esa ruta.
  5. Valida tool y legacy function por separado.
  6. Ejecuta un minimal same-route smoke test.
  7. Ejecuta el flujo completo con role logging activo.
  8. Escala con sanitized packet si todavía falla.

Ese orden mantiene clara la señal y protege la jerarquía de instrucciones. Evita el arreglo rápido de “ponerlo en user”, que puede quitar el 400 mientras debilita el control del modelo.

Preguntas frecuentes

¿Significa que mi API key está mal?

Normalmente no. Una key incorrecta falla en autenticación antes de validar roles. Este error significa que la ruta pudo leer el payload y rechazó un rol concreto.

¿Debo reemplazar system por developer en todo?

No. developer solo aplica a rutas de Chat Completions que lo soportan. Responses usa instructions, y Azure o gateways compatibles pueden tener otro contrato.

¿Es seguro mover system instructions a user?

Solo como último fallback y tratándolo como cambio de comportamiento. Un user message no suele tener la prioridad de system, developer o instructions.

¿Por qué aparece después de actualizar el framework?

El framework pudo cambiar el role mapping, insertar un instruction message oculto o pasar de function legacy a tool format moderno. Revisa el HTTP payload final.

OpenAI directo funciona, pero mi proveedor compatible falla

Entonces la diferencia está en el contrato del provider route. Guarda payload, endpoint, model alias y trace, y pregunta qué roles y tool-message shapes acepta esa ruta.

¿Qué guardo para soporte?

Request JSON desinfectado, endpoint exacto, model o deployment, API version, versiones de SDK y framework, response body, param, request ID y minimal same-route repro. Elimina secretos y datos de usuario.

Share:

laozhang.ai

One API, All AI Models

AI Image

Gemini 3 Pro Image

$0.05/img
80% OFF
AI Video

Sora 2 · Veo 3.1

$0.15/video
Async API
AI Chat

GPT · Claude · Gemini

200+ models
Official Price
Served 100K+ developers
|@laozhang_cn|Get $0.1