Summary
When UPSTREAM_PROVIDER=codex, the subscription proxy forwards the client's /v1/responses body to the ChatGPT Codex backend (https://chatgpt.com/backend-api/codex/responses) verbatim (only stream is forced to true in src/subscription_proxy.rs). But the Codex backend is stricter than the generic OpenAI Responses API and rejects two things that standard OpenAI Responses clients routinely send/omit:
max_output_tokens is rejected → HTTP 400 {"detail":"Unsupported parameter: max_output_tokens"}
instructions is required → HTTP 400 {"detail":"Instructions are required"}
As a result, off-the-shelf OpenAI Responses clients cannot use a Codex subscription through the router — every request fails with a 400.
Environment
- Router
v0.20.0 (image konard/link-assistant-router:latest)
UPSTREAM_PROVIDER=codex, credential ~/.codex/auth.json (ChatGPT OAuth), valid token (doctor → "codex subscription … (found, token OK)")
Reproduction
A standard OpenAI Responses request (this is exactly what the OpenClaw agent emits for an openai-responses provider):
curl -s "$ROUTER/v1/responses" \
-H "Authorization: Bearer la_sk_..." -H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"input": [{"role":"user","content":[{"type":"input_text","text":"hi"}]}],
"stream": true,
"store": false,
"max_output_tokens": 8192,
"reasoning": {"effort": "none"}
}'
# → 400 {"detail":"Unsupported parameter: max_output_tokens"}
Remove max_output_tokens but still omit instructions:
# → 400 {"detail":"Instructions are required"}
Add instructions and drop max_output_tokens:
-d '{"model":"gpt-5.5","instructions":"You are a test echo.","input":[{"role":"user","content":[{"type":"input_text","text":"Reply with exactly: ROUTER_OK"}]}],"store":false}'
# → 200, streamed response "ROUTER_OK" ✅
So the upstream/credential/transport are all fine — the only problem is that the router forwards a body shape the Codex backend won't accept.
Root cause
src/subscription_proxy.rs::forward_subscription_openai only does:
if provider == SubscriptionProvider::Codex {
body["stream"] = serde_json::Value::Bool(true);
}
It does not strip max_output_tokens or ensure instructions is present. (Note src/responses.rs::chat_completion_to_responses even adds max_output_tokens when projecting Chat Completions → Responses, which then hits the same 400 for the /v1/chat/completions path.)
Proposed fix
For Codex, normalize the Responses body before forwarding: force stream, strip max_output_tokens, and inject a default instructions only when the caller omitted one (preserving caller intent otherwise). PR incoming.
This makes any standard OpenAI Responses / Chat Completions client (OpenClaw, SDKs, etc.) work against a Codex subscription through the router.
Summary
When
UPSTREAM_PROVIDER=codex, the subscription proxy forwards the client's/v1/responsesbody to the ChatGPT Codex backend (https://chatgpt.com/backend-api/codex/responses) verbatim (onlystreamis forced totrueinsrc/subscription_proxy.rs). But the Codex backend is stricter than the generic OpenAI Responses API and rejects two things that standard OpenAI Responses clients routinely send/omit:max_output_tokensis rejected →HTTP 400 {"detail":"Unsupported parameter: max_output_tokens"}instructionsis required →HTTP 400 {"detail":"Instructions are required"}As a result, off-the-shelf OpenAI Responses clients cannot use a Codex subscription through the router — every request fails with a 400.
Environment
v0.20.0(imagekonard/link-assistant-router:latest)UPSTREAM_PROVIDER=codex, credential~/.codex/auth.json(ChatGPT OAuth), valid token (doctor→ "codex subscription … (found, token OK)")Reproduction
A standard OpenAI Responses request (this is exactly what the OpenClaw agent emits for an
openai-responsesprovider):Remove
max_output_tokensbut still omitinstructions:Add
instructionsand dropmax_output_tokens:So the upstream/credential/transport are all fine — the only problem is that the router forwards a body shape the Codex backend won't accept.
Root cause
src/subscription_proxy.rs::forward_subscription_openaionly does:It does not strip
max_output_tokensor ensureinstructionsis present. (Notesrc/responses.rs::chat_completion_to_responseseven addsmax_output_tokenswhen projecting Chat Completions → Responses, which then hits the same 400 for the/v1/chat/completionspath.)Proposed fix
For Codex, normalize the Responses body before forwarding: force
stream, stripmax_output_tokens, and inject a defaultinstructionsonly when the caller omitted one (preserving caller intent otherwise). PR incoming.This makes any standard OpenAI Responses / Chat Completions client (OpenClaw, SDKs, etc.) work against a Codex subscription through the router.