Si Claude Code muestra API Error: 400 due to tool use concurrency issues, el estado de la conversación asistida por herramientas es inconsistente. No empieces rotando keys, comprando cuota, cambiando de proveedor ni borrando toda la sesión.
A 15 de julio de 2026, la referencia de errores de Anthropic marca una rama estrecha que va primero: si usas Opus 4.7 u 4.8 en una versión de Claude Code anterior a v2.1.156, actualiza Claude Code. Esas versiones pueden producir el mismatch durante el uso normal de herramientas y /rewind quizá no lo elimine. En otras versiones o modelos, guarda el contexto, ejecuta /rewind o Esc dos veces hasta antes del turno roto y prueba una acción pequeña.
Si controlas el API loop, empareja cada tool_use del assistant con exactamente un tool_result, devuelve el conjunto completo en el siguiente user message y coloca todos los resultados antes del texto.
| Superficie | Primera acción segura | Señal de éxito |
|---|---|---|
| Opus 4.7/4.8 + Claude Code anterior a v2.1.156 | actualizar Claude Code y repetir una acción pequeña | la misma acción termina en el cliente actualizado |
| Otras sesiones Claude Code CLI o IDE | guardar la tarea y rebobinar antes del turno roto | un nuevo tool turn termina sin el 400 |
| Cliente Anthropic propio | auditar el último assistant tool turn y el siguiente lote de resultados | todos los IDs coinciden, el validator queda vacío y un replay funciona |
Si la actualización aplicable o un rewind limpio no salva la misma acción pequeña, detén el bucle de recuperación. Conserva versión, superficie, error exacto y forma sanitizada de los dos turnos; después abre una sesión desde un resumen humano sin importar el transcript roto, o presenta un reporte acotado.
Qué significa este 400 exacto
La frase “tool use concurrency issues” parece decir que ejecutaste demasiadas herramientas a la vez. Esa interpretación puede ser parte del problema, pero no es la mejor primera respuesta. El error encaja mejor con un estado de conversación que la API ya no puede continuar: un bloque tool_use, tool_result o thinking no coincide con lo que llega en la siguiente solicitud.
Ese estado se rompe de dos maneras. En Claude Code, una sesión larga puede acumular un tool run interrumpido, un stream detenido, un estado parcial de una extensión, una edición manual del historial o una compresión que eliminó bloques necesarios. En un cliente propio de Messages API, el chat puede parecer lógico para una persona y aun así ser inválido para la API porque falta un resultado, los resultados están divididos en varios user messages, el texto aparece antes de los resultados o los IDs no coinciden.
Por eso a veces el chat normal responde y las herramientas fallan. El texto plano no necesita el mismo emparejamiento que Read, Edit, Bash o Search. Las herramientas dependen de IDs, orden, roles y tipos de bloque. Si tratas este error como un problema genérico de red, facturación o límite, puedes pasar mucho tiempo corrigiendo la capa equivocada.
Recupera la sesión de Claude Code sin limpiar trabajo útil
Primero conserva el contexto que una persona puede reutilizar. Anota la tarea, los archivos en curso, la acción que falló, el texto exacto del error, la hora con zona horaria, si ocurrió en CLI o IDE, y si el chat ordinario todavía responde. No necesitas guardar el transcript completo. Necesitas una versión segura para continuar si la sesión actual no se puede salvar.
Después registra claude --version. Si coincide Opus 4.7/4.8 con Claude Code anterior a v2.1.156, actualiza primero: no demuestres una excepción documentada con varios /rewind. En los demás casos compara la misma acción pequeña en CLI y en VS Code, Cursor u otra integración. Así separas el estado de la extensión del historial de la sesión completa.
Usa /rewind para volver antes del turno de herramienta dañado. Claude Code también permite retroceder con Esc dos veces. Después de volver, no envíes una instrucción grande que mezcle leer archivos, editar código, correr tests y buscar documentación. Usa una sola acción pequeña. Si funciona, continúa con instrucciones limpias y relacionadas.
Abre una sesión nueva solo cuando ese camino falla. En la sesión nueva no pegues todo el transcript roto. Lleva un resumen humano de la tarea, archivos relevantes, último estado confiable y evidencia segura del error. Copiar el historial de herramientas dañado puede reproducir el mismo 400 bajo una conversación nueva.
En VS Code, Cursor e IDE, divide la prueba
La rama de IDE merece atención propia porque el síntoma suele ser engañoso: conversación normal funcionando, tools fallando. Eso no prueba que el usuario haya usado demasiadas herramientas en paralelo. Puede significar que la extensión, el puente local, el entorno remoto o el cliente guardó un estado de tool history que ya no corresponde.
El orden práctico es: registra la superficie exacta, compara una acción pequeña en CLI e IDE, revisa versiones y solo después considera reiniciar, actualizar, reinstalar o volver a una versión anterior. Un downgrade visto en discusiones antiguas puede haber sido un workaround temporal, pero no debe convertirse en una regla universal.
Si la IDE falla y CLI funciona, continúa el trabajo urgente en CLI y aísla la ruta de la extensión. Si ambos fallan en la misma sesión, rebobina la conversación antes de culpar a la extensión. Si una sesión limpia reproduce el mismo fallo con una acción mínima, entonces prepara un paquete de evidencia para soporte o para el repositorio correspondiente.
También evita convertir “volver a iniciar sesión” en la primera cura. La autenticación puede explicar otros fallos, pero este 400 exacto apunta primero a historial de herramientas o forma del request. Si el cuerpo del error no habla de auth, billing o token, no sustituyas el diagnóstico por una acción de cuenta.
Si construyes un cliente Anthropic, mira el arreglo messages
En un agent framework, integración IDE o backend runner, el fallo suele estar en el arreglo messages. El assistant puede emitir uno o varios tool_use en un turno. Tu código puede ejecutar esas herramientas en paralelo o en secuencia. Lo rígido es la forma de devolver resultados: el siguiente user message debe contener todos los tool_result correspondientes a ese turno.
Hay tres reglas centrales. Cada tool_use.id necesita un tool_result.tool_use_id correspondiente. Todos los resultados de un mismo assistant turn deben volver juntos en el siguiente user message. Si ese user message contiene resultados y texto, los bloques tool_result deben ir antes del texto. Una frase como “ya tengo los resultados” antes de los resultados puede parecer natural, pero rompe la forma esperada.
| Error del cliente | Por qué rompe | Reparación |
|---|---|---|
falta un tool_result | un tool request del assistant queda abierto | devolver un resultado por cada ID o quitar todo el tool turn dañado del replay history |
| resultados divididos en varios user messages | el set de resultados de un turno queda separado | unir todos los resultados en el siguiente user message |
texto antes de tool_result | los resultados deben aparecer primero | poner todos los tool_result antes de cualquier texto permitido |
| llamada fallida o saltada sin result | el assistant call queda abierto | devolver su matching result con is_error: true |
| ID duplicado o desconocido | no se puede unir uno a uno con el turno anterior | eliminar duplicados y rechazar IDs ajenos al turno |
| compaction elimina tool blocks | se pierde la pareja o continuidad necesaria | conservar o eliminar grupos completos tool_use / tool_result |
Al depurar, registra la forma y no los secretos. Bastan roles, block types, IDs, conteo de resultados y orden. No registres API keys, tokens, archivos privados, datos de clientes ni payloads propietarios. Un validator junto al runner puede impedir que un request mal formado llegue a la API.
Valida el turno fallido antes de repetirlo
Aísla el último assistant message que contiene tool_use del cliente y el user message inmediato. Esta función TypeScript comprueba IDs ausentes, duplicados o desconocidos, orden de bloques, resultados fallidos/saltados y un server tool pendiente:
hljs tstype Block = { type: string; id?: string; tool_use_id?: string; is_error?: boolean };
type Message = { role: "assistant" | "user"; content: Block[] };
export function auditToolTurn(
assistant: Message,
nextUser: Message,
options: { failedOrSkippedIds?: ReadonlySet<string>; hasPendingServerTool?: boolean } = {},
): string[] {
const problems: string[] = [];
const calls = assistant.content.filter((b) => b.type === "tool_use");
const results = nextUser.content.filter((b) => b.type === "tool_result");
const callIds = calls.flatMap((b) => b.id ? [b.id] : []);
const resultIds = results.flatMap((b) => b.tool_use_id ? [b.tool_use_id] : []);
const count = (ids: string[], id: string) => ids.filter((x) => x === id).length;
if (assistant.role !== "assistant") problems.push("Expected an assistant tool-use turn");
if (nextUser.role !== "user") problems.push("Tool results must be in the next user message");
for (const id of new Set(callIds)) {
if (count(callIds, id) > 1) problems.push(`Duplicate tool_use id: ${id}`);
if (count(resultIds, id) === 0) problems.push(`Missing tool_result: ${id}`);
if (count(resultIds, id) > 1) problems.push(`Duplicate tool_result: ${id}`);
}
for (const id of new Set(resultIds)) if (!callIds.includes(id)) problems.push(`Unexpected tool_result: ${id}`);
const firstNonResult = nextUser.content.findIndex((b) => b.type !== "tool_result");
if (firstNonResult >= 0 && nextUser.content.slice(firstNonResult + 1).some((b) => b.type === "tool_result")) {
problems.push("Every tool_result must appear before text or other user content");
}
for (const id of options.failedOrSkippedIds ?? []) {
const result = results.find((b) => b.tool_use_id === id);
if (result && result.is_error !== true) problems.push(`Failed or skipped call must use is_error: true: ${id}`);
}
if (options.hasPendingServerTool && nextUser.content.some((b) => b.type !== "tool_result")) {
problems.push("Pending server tool: next user message must contain client tool_result blocks only");
}
return problems;
}
Un arreglo vacío indica que ese par supera estas comprobaciones, no que los thinking blocks anteriores sigan intactos. Corrige el producer o el registro guardado y haz un solo replay; no reenvíes indefinidamente el mismo arreglo mal formado.
La ejecución paralela puede existir; la devolución no se improvisa
La ejecución paralela no está prohibida. Claude puede pedir varias herramientas en un solo assistant message, y tu cliente puede ejecutarlas al mismo tiempo si eso es seguro. También puede ejecutarlas una por una. La API no está juzgando ese scheduler interno; juzga cómo vuelve el conjunto de resultados al diálogo.
Por eso “desactivar llamadas paralelas” no es una cura completa. Si tu serializer pierde IDs, divide resultados, coloca texto antes de resultados o reescribe history, seguirá fallando aunque ejecutes una herramienta por vez. En sentido contrario, varios tools pueden ejecutarse en paralelo si el cliente espera a todos y devuelve un set completo, emparejado y ordenado.
disable_parallel_tool_use es útil cuando existe una limitación real: herramientas con side effects, sistemas externos frágiles o un runner que todavía no puede agrupar resultados. Va dentro de tool_choice, no en el nivel superior del request:
hljs json{
"tool_choice": {
"type": "auto",
"disable_parallel_tool_use": true
}
}
No reemplaza las reglas de tool_result. Incluso con un solo tool call, el resultado debe aparecer en el user message correcto y en el orden correcto.
Para producción, añade una validación cerca del tool runner: cada assistant tool_use.id tiene un result; no hay tool_result extra; los resultados de un turn están juntos; los resultados aparecen antes del texto; el replayed history no borró thinking, tool o result blocks que el modelo necesita.
Un server tool pendiente cambia el siguiente user message
Normalmente puedes poner texto permitido después de todos los tool_result. Pero si el mismo assistant turn mantiene un server tool sin resolver, el siguiente user message debe contener solo los tool_result del cliente. Un texto al final puede cerrar el turno antes de tiempo y causar un 400 que menciona el server tool pendiente.
| Estado del assistant turn | Contenido permitido en el siguiente user message |
|---|---|
| Solo client tools | todos los resultados primero; después, texto permitido |
| Client tools + pending server tool | solo los client tool_result correspondientes |
| Sin client calls pendientes | un user message normal según el resto del contrato |
No inventes un resultado de cliente para cerrar el server tool. Conserva el server block, devuelve únicamente los resultados que ejecutaste y deja continuar el loop soportado.
Por qué las sesiones largas y las interrupciones lo hacen volver
Una sesión larga de Claude Code no es solo texto. También contiene estado mecánico. Si una herramienta fue detenida a mitad, un stream se cortó, una extensión guardó estado parcial, alguien editó el historial o un framework compactó messages sin conservar los grupos completos, el siguiente request puede quedar inválido aunque el resumen parezca razonable.
Cuando el error vuelve, no preguntes solo “¿se ejecutó en paralelo?”. Pregunta qué ocurrió con el transcript: ¿se detuvo un tool run? ¿se pegó un historial viejo en una sesión nueva? ¿la compresión quitó un result? ¿cambió el set de tools? ¿un user message empezó con texto antes de resultados? ¿la IDE reenvía una conversación antigua?
| Situación | Riesgo | Recuperación más segura |
|---|---|---|
| tool run detenido | queda un estado parcial | rebobinar antes del tool turn y probar una acción pequeña |
| historial editado o compactado | se eliminan tool/thinking blocks necesarios | empezar desde un resumen humano, no desde transcript |
| solo falla la IDE | estado de extensión o bridge dañado | comparar CLI y guardar versión más acción mínima |
| app reproduce stored messages | serializer cambia IDs u orden | validar el exact message array antes del API call |
Pedir al modelo “intenta otra vez”, “ve más lento” o “usa una sola herramienta” puede esquivar el síntoma una vez, pero no arregla un historial ya inválido. La reparación estable es volver al checkpoint correcto o corregir la serialización.
No todos los Claude 400 usan esta rama
El texto exacto importa. Un 400 con extra inputs, unsupported role, invalid system, malformed JSON o parámetros de modelo inválidos sigue siendo un problema de formato, pero no necesariamente un mismatch de tool history. Usa la rama de este documento solo cuando el error realmente diga API Error: 400 due to tool use concurrency issues o cuando el contexto indique una historia de tool/result rota.
| Síntoma | Rama más adecuada |
|---|---|
| 400 con extra inputs, unsupported role, invalid system o malformed JSON | validación de schema y parámetros |
| 400 después de tool calls, historial editado o sesión larga | mismatch de tool/result o thinking block |
| 429 o rate limit reached | guía específica de rate limit para allowance, límites y quota |
529 overloaded_error | guía específica de Claude 529 para capacidad y retry acotado |
| 500 o 504 | error de servidor, timeout, request_id y estado |
API Error: Connection error | guía específica de conexión para red, VPN, DNS, TLS o timeout |
| auth, billing, credits | cuenta, proyecto, organización, pago o credenciales |
Claude Status tiene valor como comprobación lateral. En la revisión del 15 de julio de 2026 a las 13:48 UTC+8, el resumen oficial mostraba Claude API y Claude Code operativos y sin incidente activo. Es una comprobación fechada, no una garantía actual ni prueba de que tu sesión, extensión o arreglo messages estén sanos. Si fallan varias rutas a la vez, revisa el estado en vivo; si el exact 400 sigue, vuelve a versión, error body e historial.
Antes de reportar, conserva evidencia útil
Un paquete útil no necesita secretos. Incluye versión de Claude Code, versión de la extensión, superficie usada, texto exacto del error, timestamp con zona horaria, tipo de operación, si el chat normal funciona, si /rewind o Esc dos veces ayudó, comparación CLI versus IDE, y una muestra de message shape sin datos sensibles si el cliente es tuyo.
No adjuntes API key, token, archivos privados, datos de clientes ni transcript completo confidencial. Para un bug de serializer, lo importante es qué tool_use IDs estaban en el assistant turn, qué tool_result IDs volvieron en el siguiente user message, si faltó alguno, si fueron divididos o si quedaron después del texto.
La misma disciplina previene recurrencias. No edites tool turns antiguos a mano. No insertes resúmenes naturales antes de tool_result. No dividas los resultados de un assistant turn en varios messages. No trates una página de estado verde como prueba de que el request shape es válido. Si necesitas empezar limpio, lleva un resumen humano y referencias seguras, no el historial roto.
FAQ
¿Uso /rewind, Esc dos veces, /clear o una sesión nueva?
Primero /rewind o Esc dos veces. Son las opciones que intentan volver antes del tool turn dañado conservando más contexto útil. Usa /clear o una sesión nueva solo si una acción pequeña sigue fallando después.
¿Por qué funciona el chat normal pero fallan los archivos?
El chat normal no necesita emparejar tool calls y results. Leer, editar, ejecutar bash o buscar dependen del estado de herramientas. Si IDs, orden o block types no coinciden, esas acciones fallan aunque el texto siga saliendo.
¿Significa que ejecuté demasiadas herramientas en paralelo?
No necesariamente. Puede ocurrir después de un turno multi-tool real, pero también por interrupciones, edición de historial, compaction, estado de extensión, resultados faltantes, resultados divididos o texto antes de tool_result.
¿Debo rotar la API key?
No como primer paso. Una key nueva no repara un message history mal formado. Solo entra en credenciales si el error body o la evidencia de cuenta apunta a auth.
¿Conviene desactivar parallel tool use?
Solo si tu cliente no puede agrupar resultados de forma segura o si las herramientas tienen side effects. Coloca disable_parallel_tool_use: true dentro de tool_choice. Aun así, el orden y el emparejamiento de tool_result siguen siendo obligatorios.
¿Qué devuelvo si una herramienta falla o se omite?
Devuelve un tool_result para su tool_use_id, marca is_error: true y añade una descripción segura. No elimines la llamada del lote: la completitud del protocolo no significa que todas las operaciones hayan tenido éxito.
¿Por qué el texto tras los resultados puede causar un 400 de server tool?
El texto suele estar permitido, pero no mientras el mismo assistant turn conserve un pending server tool. En ese estado mixto, el siguiente user message debe contener solo los client tool_result.
¿Claude Status importa?
Sí, pero como sanity check. Si muchas rutas fallan al mismo tiempo, revísalo. Si el exact 400 permanece, la reparación central sigue siendo rewind o message-shape repair.
¿Qué pongo en un issue o ticket?
Versión, superficie, texto exacto, hora, tipo de operación, si funciona el chat normal, si ayudó /rewind, comparación CLI versus IDE y message shape sin secretos. No incluyas keys, tokens, archivos privados ni transcript completo.
¿Cuándo dejo de intentar salvar la sesión?
Después de /rewind y una o dos acciones pequeñas que reproduzcan el mismo exact error. Guarda evidencia, abre una sesión limpia desde un resumen humano y evita importar el tool history dañado.



