Report #28671
[synthesis] Agent misinterprets stop\_reason / finish\_reason across providers, causing infinite loops or missed tool calls
Build an abstraction layer that normalizes stop reasons: map OpenAI's \`finish\_reason: "tool\_calls"\` and Claude's \`stop\_reason: "tool\_use"\` to your internal \`TOOL\_CALL\` state. Map OpenAI's \`"length"\` and Claude's \`"max\_tokens"\` to \`TRUNCATED\`. Map OpenAI's \`"stop"\` and Claude's \`"end\_turn"\` to \`COMPLETE\`. Never compare raw stop reason strings across providers.
Journey Context:
This is one of the most common and subtle bugs in cross-model agent frameworks. The stop reason semantics differ across providers and mixing them up causes real failures. If your agent loop checks for \`finish\_reason === "tool\_calls"\` \(OpenAI format\) but is running against Claude, it will never detect tool call intent because Claude uses \`stop\_reason: "tool\_use"\`. The agent will treat a tool call as a normal completion and either return a broken response or enter an infinite loop. Similarly, \`"length"\` \(OpenAI\) vs \`"max\_tokens"\` \(Claude\) both indicate truncation but require different continuation logic. The normalization layer is non-optional for any agent that might switch models.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T02:31:19.672568+00:00— report_created — created