Skip to content

e2e-chatbot-app-next: duplicate/unresolved tool parts can reach the provider and trigger 400s (tool_use / tool_result mismatch) #254

Description

@AndreasJaeck

Summary

We run a fork of e2e-chatbot-app-next against Databricks model serving endpoints and periodically hit provider 400s caused by a tool_use / tool_result mismatch in the converted model messages.

Caveat up front: I have not reproduced this against the unmodified template. Please treat this as a field report plus an inferred mechanism rather than a confirmed template bug. I am filing it because the relevant code path looks unguarded upstream and the mitigation is cheap.

Symptom

Anthropic-family endpoints reject the request with a 400 complaining that tool_use blocks have no corresponding tool_result blocks. There is an OpenAI / AI Gateway flavour of the same failure. It appears mid-conversation, after tool calls have happened, and tends to recur for the affected thread because the bad state has been persisted.

Inferred mechanism

server/src/routes/chat.ts L251 calls:

const modelMessages = await convertToModelMessages(uiMessages);

with no options and no de-duplication. uiMessages is assembled from persisted DB messages and/or requestBody.previousMessages (L169-198). Two states appear able to survive that round-trip:

  1. Duplicate tool-<name> parts sharing one toolCallId, at different states.
  2. Parts stuck at input-available — a call with no result, e.g. the stream ended early, the tool errored, or an approval was denied.

Either one produces a tool_use with no matching tool_result once converted.

Mitigation we use

Two changes, both cheap and drop-only:

  1. De-duplicate UI tool parts by toolCallId before conversion, keeping the most-resolved state (output-available > output-error > input-available > input-streaming). Never synthesise a part, only drop.
  2. Pass ignoreIncompleteToolCalls: true to convertToModelMessages.

Since adding those we have not seen the 400 recur.

Ask

Would you consider guarding this path upstream? Happy to send a PR if that would be useful. Equally, if you already know this is handled elsewhere or is specific to how our fork persists messages, feel free to close.

Version

Template e2e-chatbot-app-next at ab30424be796009d34a0b760c25395211fe08e91

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions