Summary
Codex standalone WebSearch keys /alpha/search requests only by the Claude Code session ID. Claude Code Workflow children share that parent session ID but carry distinct direct Agent IDs, so concurrent sibling searches contend on one upstream search session and frequently return 409 Search session is busy after the proxy's internal wait.
This is the same ownership topology addressed for Codex continuation in #95/#96, but the standalone-search path still discards the parsed ConversationIdentity.
Affected snapshot: main@55bf0b5818b461e1860964809726f99d2fd52c10 (also observed on release 0.1.35 with Claude Code 2.1.260/2.1.263).
Observed topology
A Workflow has one Claude session and each child has a stable direct Agent ID:
- Agent A:
session=S, agent=A
- Agent B:
session=S, agent=B
The proxy already parses those headers into ConversationIdentity::Agent(S, A) and ConversationIdentity::Agent(S, B) and uses that identity for continuation ownership.
Current standalone-search path
dispatch_request parses ConversationIdentity and passes it to CodexProvider::handle_messages_with_conversation_identity. In the standalone-search branch, however, src/providers/codex/mod.rs calls:
search::build_search_request(&body, &resolved.model, ctx.session_id.as_deref())
src/providers/codex/search.rs then uses that shared session ID as SearchRequest.id. The direct Agent ID never participates.
Production evidence
Under several concurrent research workflows across three authenticated proxy accounts:
- 5-minute sample before mitigation: 644 completed requests, 478 status 409, 163 status 200;
- 409 latency: p50 15.1 s, p95 24.3 s;
- exact upstream message:
Search session is busy after waiting with exponential backoff for the current turn to finish. Retry after a short delay.
A temporary downstream mitigation that removed the shared session header only for forced standalone searches reduced the observed search-session 409 count to zero, but makes each search stateless. The intended fix is stable per-Agent ownership.
No credentials, request bodies, or target data are included here.
Expected behavior
Standalone search should follow the existing identity ownership contract:
- Main: session ID;
- Agent:
(session ID, direct Agent ID);
- nested Agent: its direct child Agent ID; parent ID remains validation-only;
- missing/malformed/ambiguous identity: stateless safe fallback.
Two turns from one Agent should retain a stable search owner. Sibling Agents sharing one parent session must receive distinct owners. Ordinary generation, continuation, count-tokens, other providers, and model routing should remain unchanged.
Proposed tests
- Same Agent and session produce the same search owner.
- Sibling Agents under one session produce different owners.
- Same Agent ID under different sessions produces different owners.
- Main preserves the existing session owner.
- Invalid or absent identity remains stateless.
- Standalone-search provider path receives that owner; ordinary generation remains unchanged.
I am preparing a focused PR with the tests first and the minimal identity plumbing.
Summary
Codex standalone WebSearch keys
/alpha/searchrequests only by the Claude Code session ID. Claude Code Workflow children share that parent session ID but carry distinct direct Agent IDs, so concurrent sibling searches contend on one upstream search session and frequently return409 Search session is busyafter the proxy's internal wait.This is the same ownership topology addressed for Codex continuation in #95/#96, but the standalone-search path still discards the parsed
ConversationIdentity.Affected snapshot:
main@55bf0b5818b461e1860964809726f99d2fd52c10(also observed on release 0.1.35 with Claude Code 2.1.260/2.1.263).Observed topology
A Workflow has one Claude session and each child has a stable direct Agent ID:
session=S,agent=Asession=S,agent=BThe proxy already parses those headers into
ConversationIdentity::Agent(S, A)andConversationIdentity::Agent(S, B)and uses that identity for continuation ownership.Current standalone-search path
dispatch_requestparsesConversationIdentityand passes it toCodexProvider::handle_messages_with_conversation_identity. In the standalone-search branch, however,src/providers/codex/mod.rscalls:src/providers/codex/search.rsthen uses that shared session ID asSearchRequest.id. The direct Agent ID never participates.Production evidence
Under several concurrent research workflows across three authenticated proxy accounts:
Search session is busy after waiting with exponential backoff for the current turn to finish. Retry after a short delay.A temporary downstream mitigation that removed the shared session header only for forced standalone searches reduced the observed search-session 409 count to zero, but makes each search stateless. The intended fix is stable per-Agent ownership.
No credentials, request bodies, or target data are included here.
Expected behavior
Standalone search should follow the existing identity ownership contract:
(session ID, direct Agent ID);Two turns from one Agent should retain a stable search owner. Sibling Agents sharing one parent session must receive distinct owners. Ordinary generation, continuation, count-tokens, other providers, and model routing should remain unchanged.
Proposed tests
I am preparing a focused PR with the tests first and the minimal identity plumbing.