Anthropic 400 `all content must be type text if is_error is true` after a parallel `handoff` + tool call (suspected: `tool_reference` content combined with `is_error`)
Los mantenedores suelen responder en 1 día
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 48/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Activo
- Stack tecnológico
- go
- Área
- backend-api-design
Línea de trabajo
Read pkg/model/provider/anthropic/beta_converter.go, especially convertBetaToolResultBlockWithDeferred, and trace how pkg/runtime/loop.go:918 sets the deferral load point. First add a regression test for a parallel tool batch where the deferred result has IsError set; the issue reports this scenario as suspected and unverified. Done means confirming the failing conversion path and ensuring the resulting request is accepted by the Anthropic API.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Version: docker-agent v1.151.0 (the converter code on main is identical), model anthropic/claude-opus-5-5, Beta API with adaptive thinking.
Summary
A session died with a non-retryable 400 on the first model call after a handoff:
error receiving from stream: HTTP 400: invalid_request_error:
messages.66.content.1.tool_result: all content must be type `text` if `is_error` is true
Impact
The whole session becomes unusable. The bad tool_result is part of the conversation history, so every later model call resends it and fails the same way. There is no way to continue or recover in place.
All the work in that session is lost: the context built up over a long multi-agent run, the intermediate results, and the tokens spent producing them. In our case the session was about 100 messages deep, with many tool calls and a long transfer_task result. The failure comes from an unrelated tool that happened to be last in the batch, not from anything the user or agent did wrong, and it gives no warning beforehand. The only remedy is to start a new session from scratch.
Confidence: the log is not conclusive
The debug log does not prove this diagnosis. The log records tool names and output lengths, but not message bodies or is_error flags. What I know and what I'm inferring:
Confirmed from the log
rootissued two parallel tool calls in one turn:handoff(toplanner, 673-char output) andupdate_todos(50-char output).planner's first request had 67 messages and was rejected atmessages.66.content.1, which is the second result in the last user message. By ordering, that is theupdate_todosresult.- The same session earlier had a successful
handoff+create_todospair (2919-char output). - This is the only occurrence in the retained logs, which cover about one day.
Inferred, not confirmed
- That the
update_todosresult hadIsError == true. I never saw its content. The 50-char length and the 400 are consistent with a short error such as an unknown todo id, but a short success message would also be that length. - That the failing block is a
tool_referenceresult. I'm inferring this from the code path below and from the error text. The log does not show which branch of the converter ran. - That the
update_todoscall id was the deferral load point. It is only the expected value givenlastToolCallID(messages).
Not done
- I have not reproduced this in isolation or in a test.
If a maintainer can confirm which branch of convertBetaToolResultBlockWithDeferred produced content.1, or has a debug flag that dumps the outgoing request, that would settle it.
Suspected root cause (from reading the code)
plannerexposes tools that weren't in the session's initial tool set.DeferralTracker.MarkAt(pkg/tools/deferrals.go) marks themDeferred, withDeferredAtToolCallIDset to the id passed in atpkg/runtime/loop.go:918:r.toolDeferrals.MarkAt(sess.ID, lastToolCallID(messages), agentTools). That id is the last tool message in the history, which here would be theupdate_todoscall that ran alongside the handoff.- In
convertBetaToolResultBlockWithDeferred(pkg/model/provider/anthropic/beta_converter.go), a result whose call id has deferred names becomes atool_resultwhose content istool_referenceblocks, withIsErrorcopied from the message:return anthropic.BetaContentBlockParamUnion{OfToolResult: &anthropic.BetaToolResultBlockParam{ ToolUseID: msg.ToolCallID, IsError: param.NewOpt(msg.IsError), Content: content, // tool_reference blocks }}, nil - If that call's result is an error, the request has
is_error: truewith non-text content, which the API rejects with exactly this message.
If the diagnosis is right, whether a session dies depends on whether an unrelated tool that happened to be last in the batch succeeded.
Suggested fix (if the diagnosis holds)
Any of these would work:
- In
convertBetaToolResultBlockWithDeferred, skip thetool_referencebranch whenmsg.IsErroris true and emit the normal text result. - In the runtime, attach the load point to the last non-error tool call, or to the
handoffcall itself, rather than blindly tolastToolCallID. - Add a regression test for a parallel tool batch where the last result has
IsError: trueandDeferredAtToolCallIDpoints at it.
Because the failure is unrecoverable, a defensive sanitize step before sending would also help. It would downgrade any is_error result with non-text content to a plain text result instead of failing the whole session.
Suspected repro (untested)
- Multi-agent config where
roothas ahandofftoplanner, andplannerhas tools thatrootdoesn't, so they're deferred after the handoff. - Have
rootemithandoffand a tool that returns an error (for exampleupdate_todoswith an unknown id) in the same assistant turn, with the failing tool last. planner's first request should return the 400.
- Lenguaje dominante
- Go
- Estrellas
- 4.6k
- Forks
- 522
- Merge medio
- 8 h 43 min
- PR fusionados (30 d)
- 239
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Incluye un Dockerfile o un archivo de Docker Compose
- Sin plantilla de pull request
- Sin guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de docker/docker-agent
-
Error when trying to run sandbox example?Posiblemente ocupada @dgageot la tomó hace 1 día. Abiertoarea/cli
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
docker/docker-agent#4672 · 1 comentario · 1 reacción · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
pkg/hooks tests fail with a misleading assertion when jq is not installedPosiblemente ocupada @dwin-gharibi la tomó hace 58 días. Abiertoarea/testing
Dificultad 2/5 1-3 horas Aptitud para principiantes 86/100
docker/docker-agent#4052 ·
Los mantenedores suelen responder en 1 día
-
`remove_directory` and `create_directory` hide work they already did when a later path failsPosiblemente ocupada @dwin-gharibi la tomó hace 77 días. Abiertoarea/tools
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
docker/docker-agent#3933 ·
Los mantenedores suelen responder en 1 día
-
`edit_file` silently edits the first match when `oldText` occurs more than oncePosiblemente ocupada @dwin-gharibi la tomó hace 77 días. Abiertoarea/tools
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
docker/docker-agent#3929 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
[Flaky test] TestA2AServer_MultipleRequestsPosiblemente ocupada @aheritier la tomó hace 1 día. Abiertoarea/a2a area/testing flaky-test
docker/docker-agent#4687 · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de docker/docker-agent
Issues similares
-
native
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
prime-radiant-inc/evener#4839 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 85/100
521xueweihan/HelloGitHub#4073 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
goharbor/terraform-provider-harbor#655 · 1 comentario ·
-
bug needs-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
syncthing/syncthing#10935 · 1 comentario ·
Los mantenedores suelen responder en 1 día