Skip to content

fix: sanitize tool-call ids and map upstream errors to real status codes - #2

Merged
AstroQore merged 1 commit into
mainfrom
fix/tool-call-ids-and-error-status
Sep 3, 2026
Merged

fix: sanitize tool-call ids and map upstream errors to real status codes#2
AstroQore merged 1 commit into
mainfrom
fix/tool-call-ids-and-error-status

Conversation

@AstroQore

Copy link
Copy Markdown
Owner

Two defects an OpenAI client trips over, both found while verifying a live deployment end to end (16 models, multi-round agent loops in both OpenAI and Anthropic shapes, direct and behind CLIProxyAPI).

Tool-call ids could contain a literal newline

McpArgs.tool_call_id was passed through verbatim. Grok models return two ids joined by a newline:

call-eeb09c02-8944-43bb-8ed4-0d2c8a99c659-0\nfc_2734bed6-1c43-9881-bebb-7ab1dec49061_0

The caller round-trips that string as tool_call_id, so anything re-encoding it under a stricter schema rejects it — Anthropic's tool_use_id pattern, for one. Composer's ids were already clean, which is why this only shows up on some models.

Everything outside [A-Za-z0-9_-] now folds into _, and an id is generated when upstream sends an empty one rather than only when it sends none. Multi-round agent loops still replay correctly with the sanitized form.

Every upstream failure was a 502

Quota exhaustion is the case that matters. Reported as a bad gateway it reads as a dead upstream, so clients and aggregators give up instead of backing off and retrying.

The Connect error code now rides on the done event, and the codes a caller can act on differently are mapped — chiefly resource_exhausted429 / rate_limit_exceeded. Unrecognised codes stay 502 rather than being dressed up as client errors. A non-200 upstream response and our own turn timeout get codes too (deadline_exceeded504).

Verified against a quota-exhausted model:

HTTP 429
{"error":{"message":"resource_exhausted: Error","type":"rate_limit_exceeded","code":"resource_exhausted"}}

Same through CLIProxyAPI, which then retries — intermittent -fast model failures that previously surfaced to the caller now recover on their own.

Docs

The READMEs advertise a multi-account failure cooldown that is not wired: AccountPool.markFailure has no callers. Corrected to describe the round-robin that actually runs, rather than shipping a promise the code does not keep. Building the failover for real is a separate change.

🤖 Generated with Claude Code

Two things an OpenAI client trips over, both found while verifying a live
deployment end to end.

Tool-call ids were passed through verbatim from `McpArgs.tool_call_id`, and grok
models return two ids joined by a literal newline
(`call-<uuid>-0\nfc_<uuid>_0`). The caller round-trips that string as
`tool_call_id`, so anything that re-encodes it under a stricter schema rejects
it — Anthropic's `tool_use_id` pattern, for one. Fold everything outside the id
alphabet into `_`, and generate an id when upstream sends an empty one rather
than only when it sends none. Composer's ids were already clean; multi-round
agent loops still replay correctly with the sanitized form.

Every upstream failure was reported as `502`. Quota exhaustion is the case that
matters: as a bad gateway it reads as a dead upstream, so clients and
aggregators give up instead of backing off. Carry the Connect error code on the
done event and map the codes a caller can act on differently — chiefly
`resource_exhausted` -> `429 rate_limit_exceeded`. Unrecognised codes stay
`502` rather than being dressed up as a client error. Verified against a
quota-exhausted model: `429` at the gateway and through CLIProxyAPI, which then
retries instead of surfacing the failure.

Also correct the READMEs: the multi-account cooldown they advertise is not
wired — `AccountPool.markFailure` has no callers — so describe the round-robin
that actually runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T17:30:30.108067Z 7f593d3 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@AstroQore
AstroQore merged commit b5b994b into main Sep 3, 2026
1 check passed
@AstroQore
AstroQore deleted the fix/tool-call-ids-and-error-status branch September 3, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant