✦ Continue with Google
AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

Duplicate Tool_calls In Content_blocks When Using OpenAI Responses API Streaming

When using ChatOpenAI with use_responses_api=True and streaming=True, partial function call blocks (fc_ prefix, empty args) accumulate in content, while final tool calls (call_ prefix, full args) appear in tool_calls. AIMessage.content_blocks merges both without recognizing they represent the same logical call, causing duplicates and API errors after serialization.

highConfidence 85%LangChainAffected V1.3.1

Origin Analysis

AIMessage.content_blocks property deduplicates tool calls solely by 'id'. Streaming partial tool calls have IDs like 'fc_xxx' while final tool calls have 'call_xxx', so they are treated as distinct. The partial blocks are meant to be transient and should not be persisted alongside the final tool calls.
1. Create an AIMessage with content containing dict blocks of type 'tool_call' with ids 'fc_12345' and 'fc_67890' and empty args. 2. Set tool_calls to final calls with ids 'call_abc123' and 'call_def456' and non-empty args. 3. Access msg.content_blocks and observe both fc_ and call_ blocks appear. 4. Serialize using content_blocks and deserialize; tool_calls now contains 4 entries including fc_ ids. 5. Attempt to match tool responses; fc_ ids have no corresponding ToolMessage, leading to API error 'No tool output found for function call fc_xxx'.

Fixing Code Block

Edge Case Audit

This fix relies on the specific 'fc_' prefix used by OpenAI; if the provider changes the prefix or emits valid final tool calls with empty args and 'fc_' prefix, legitimate calls could be dropped. A more robust solution is to modify the langchain-openai integration to avoid storing partial blocks in content entirely. Rollback: revert this change and instead patch the integration to clear partial content after streaming completes.

Ecosystem Topology