OpenAI-compatible providers: non-standard /v1/models key + no Responses API → workaround with codex-relay + stream:false

Open 💬 2 comments Opened Aug 7, 2026 by drutsan
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

OpenAI-compatible providers with non-standard /v1/models + no Responses API support

Two-part problem

Any provider that (1) returns {"data":[...]} from /v1/models instead of {"models":[...]} and (2) only exposes Chat Completions, not the Responses API, cannot be used with Codex CLI out of the box. OneProvider (api.oneprovider.dev) is a concrete example, but the pattern applies broadly.

---

Problem 1 — Wrong /v1/models response key

Expected (OpenAI standard):

{ "object": "list", "models": [...] }

Actual (OneProvider and others):

{ "data": [...], "object": "list" }

Codex CLI fails to initialise when model_catalog_json is set, or silently cannot discover models:

Error loading configuration: failed to parse model_catalog_json path … as JSON: invalid type: map, expected a sequence

Provider support confirmed the mismatch is a known bug on their side, not yet fixed, and noted it affects any OpenAI-compatible provider, not just theirs.

---

Problem 2 — Provider only exposes Chat Completions, not Responses API

Codex CLI with wire_api = "responses" calls /v1/responses.
OneProvider (and many third-party proxies / cheaper API resellers) only implement /v1/chat/completions.

Direct curl to /v1/responses → immediate timeout (HTTP 000).
Streaming Chat Completions → first chunk arrives, then hangs (Caddy 120 s hard timeout on provider side).

---

Working solution: codex-relay + stream: false

codex-relay is a Rust bridge that translates Codex's Responses API calls into Chat Completions calls. The critical extra step for providers with streaming issues is forcing non-streaming via --upstream-extra-params.

1. Install codex-relay

cargo install codex-relay
# or: pip install codex-relay

2. Create a wrapper script

~/.local/bin/oneprovider-relay.sh:

#!/bin/bash
API_KEY=$(security find-generic-password -s 'codex-oneprovider-api-key' -w 2>/dev/null)
exec codex-relay \
  --upstream https://api.oneprovider.dev/v1 \
  --api-key "$API_KEY" \
  --port 8446 \
  --upstream-extra-params '{"stream": false}'
chmod +x ~/.local/bin/oneprovider-relay.sh
Why stream: false? Without it, codex-relay sends a streaming Chat Completions request to the upstream. Providers with a hard proxy timeout (e.g. Caddy 120 s) deliver the first SSE chunk and then hang, causing a timeout. Forcing non-streaming returns a single JSON response that completes well within the timeout window (~8-16 s in testing).

3. macOS auto-start (launchd)

~/Library/LaunchAgents/com.user.oneprovider-proxy.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.oneprovider-proxy</string>
    <key>ProgramArguments</key>
    <array>
        <string>/path/to/oneprovider-relay.sh</string>
    </array>
    <key>RunAtLoad</key><true/>
    <key>KeepAlive</key><true/>
    <key>StandardOutPath</key>
    <string>/tmp/oneprovider-relay.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/oneprovider-relay-error.log</string>
</dict>
</plist>
launchctl load ~/Library/LaunchAgents/com.user.oneprovider-proxy.plist

4. Codex profile

~/.codex/oneprovider.config.toml (or codex1.config.toml):

model = "gpt-5.6-sol"
model_provider = "oneprovider"
model_reasoning_effort = "max"

[model_providers.oneprovider]
name = "OneProvider"
base_url = "http://127.0.0.1:8446/v1"   # relay, not the provider directly
wire_api = "responses"

[model_providers.oneprovider.auth]
command = "/usr/bin/security"
args = ["find-generic-password", "-s", "codex-oneprovider-api-key", "-w"]
timeout_ms = 5000
refresh_interval_ms = 0
codex --profile oneprovider

---

Verified test results

GET  /v1/models     → HTTP 200  ~0.4 s  ✅  {"object":"list","models":[19 entries]}
POST /v1/responses  → HTTP 200  ~8-16 s ✅  {"output":[{"content":[{"text":"OK"}]}]}

(Latency is the provider's own, not the relay's overhead.)

---

Suggested Codex-side fix

A small built-in compatibility shim would let providers like this work without any local relay:

[model_providers.oneprovider]
models_list_key = "data"   # fall back if "models" key is absent

Or Codex could silently accept both {"models":[...]} and {"data":[...]} when the root object is "list".

---

Related

  • #14743 — Responses API format issues with custom providers
  • #987 — Can't use other provider model
  • codex-relay — the relay used in this workaround

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 21 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #37122

Powered by Codex Action

Triumph1701 · 8 days ago

The /v1/models response shape and the Responses-versus-Chat split are compatibility checks I\x27d keep explicit when evaluating a hosted endpoint. A provider can be reachable yet still fail at discovery or request translation. Disclosure: I\x27m building Your Model, an OpenAI-compatible multi-model API platform for developers. I can add some test credits for a side-by-side compatibility check.