--- title: "Codex CLI — Configuration with OmniRoute" version: 3.8.50 lastUpdated: 2026-08-18 --- # Codex CLI — Configuration with OmniRoute Complete guide for using the Codex CLI pointed at OmniRoute as an OpenAI-compatible backend. --- > **TOML is the only effective format.** Modern Codex reads `~/.codex/config.toml` > exclusively (verified against codex-cli 0.147.0: `codex --help` documents > `-c/--config` overrides "loaded from `~/.codex/config.toml`"). The old > `~/.codex/config.yaml` belonged to the legacy npm CLI and is silently ignored. > The dashboard generator (`/api/cli-tools/apply`, tool `codex`) writes TOML with a > conservative merge — existing keys and other provider blocks are preserved, the > API key stays in `OMNIROUTE_API_KEY` (never in the file), and a leftover legacy > `config.yaml` is reported as a migration note without being touched. ## Ready-to-paste config.toml Replace `` and `` with your values: ```toml # ~/.codex/config.toml model = "cx/gpt-5.5" model_provider = "omniroute" model_reasoning_effort = "xhigh" model_context_window = 400000 model_auto_compact_token_limit = 350000 tool_output_token_limit = 32768 # history storage cap per tool call [model_providers.omniroute] name = "OmniRoute" base_url = "http://:20128/v1" env_key = "OMNIROUTE_API_KEY" requires_openai_auth = false wire_api = "responses" ``` ```bash # ~/.bashrc or ~/.zshrc — actual key value, never in config.toml export OMNIROUTE_API_KEY="" ``` ### macOS: Codex bundled inside the ChatGPT app If you installed Codex through the ChatGPT desktop app, the `codex` binary may exist only inside the app bundle and not yet be on your shell `PATH`. Add the resources directory to your shell startup file: ```bash export PATH="/Applications/ChatGPT.app/Contents/Resources:$PATH" ``` Open a new shell, then verify: ```bash command -v codex codex --version ``` ### Local unauthenticated OmniRoute: placeholder key is enough Codex validates that the environment variable named by `env_key` exists **before** the first request leaves the CLI. If your **local** OmniRoute instance does not require auth, any non-empty placeholder works: ```bash export OMNIROUTE_API_KEY="${OMNIROUTE_API_KEY:-local}" ``` Use a real key instead when your OmniRoute server is protected or remote. > **Common host options** > > | Access | URL | > | ------------- | ----------------------------- | > | Local network | `http://192.168.0.1:20128/v1` | > | Tailscale | `http://100.x.x.x:20128/v1` | > | Loopback | `http://localhost:20128/v1` | --- ## `wire_api = "responses"` — why it works for all models Codex CLI deprecated `wire_api = "chat"` (Chat Completions) in February 2026 and now **requires** `wire_api = "responses"` (OpenAI Responses API). Setting `wire_api = "chat"` causes an immediate startup crash since v0.138. Many providers, including GLM and Kimi, still expose only a Chat Completions endpoint. DeepSeek V4 now exposes a native Responses API as well as an Anthropic-compatible endpoint; OmniRoute uses Responses by default and lets each DeepSeek connection select Anthropic compatibility. **OmniRoute solves this transparently:** ``` Codex CLI → wire_api = "responses" → POST /v1/responses (OmniRoute) → OmniRoute selects the provider's native protocol and translates when needed → POST /responses (DeepSeek V4) or /chat/completions (Mistral / GLM / Kimi / others) ``` You never need a separate translation proxy when using OmniRoute. **All models use `wire_api = "responses"`** — OmniRoute handles the rest. > **`wire_api` is the default** — the field defaults to `"responses"` and can be omitted entirely from `config.toml`. Only ever set it explicitly if you're documenting intent. --- ## Context window and compaction ### Token configuration fields | Field | Description | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `model_context_window` | Total token budget for the active model. Set to the model's advertised limit. | | `model_auto_compact_token_limit` | Threshold that triggers automatic history compaction. **Maximum: 90% of `model_context_window`** — values above 90% are silently ignored. | | `tool_output_token_limit` | Cap on tokens stored per tool call output in history. Prevents a single large tool response from filling the window. **This is not the max output** — it is a history storage cap. | | `compact_prompt` | Inline override for the system prompt used during compaction (v0.138+). | > **Note on `model_max_output_tokens`**: This field is **not part of the Codex CLI config schema** (absent from the Codex Rust codebase). It is silently ignored if set. Do not rely on it — use `tool_output_token_limit` to control how much tool output is stored in history. ### Context windows by model | Model | OmniRoute ID | Context window | `auto_compact` | `tool_output_limit` | | ------------------------------------ | ------------------------------------ | ---------------------- | -------------- | ------------------- | | GPT-5.5 | `cx/gpt-5.5` | 400k reliable (1M max) | 350,000 | 32,768 | | Kimi K2.7 (thinking) | `kmc/kimi-k2.7` | 131,072 | 112,000 | 32,768 | | Kimi K2.6 | `kmc/kimi-k2.6` | 131,072 | 112,000 | 32,768 | | GLM-5.2 / 5.2-max (thinking) | `glm/glm-5.2` | 131,072 | 112,000 | 32,768 | | MiMo V2.5 Pro (thinking) | `opencode-go/mimo-v2.5-pro` | 131,072 | 112,000 | 32,768 | | Qwen 3.7 Plus (thinking) | `opencode-go/qwen3.7-plus` | 32,768 | 28,000 | 16,384 | | DeepSeek V4 Pro (OllamaCloud) | `ollamacloud/deepseek-v4-pro` | 131,072 | 112,000 | 32,768 | | DeepSeek V4 Pro | `ds/deepseek-v4-pro` | 1,000,000 | 900,000 | 65,536 | | MiMo V2.5 | `opencode-go/mimo-v2.5` | 131,072 | 112,000 | 32,768 | | Gemma 4 31B (OllamaCloud) | `ollamacloud/gemma4:31b` | 32,768 | 28,000 | 16,384 | | Nemotron 3 Super (OllamaCloud) | `ollamacloud/nemotron-3-super` | 32,768 | 28,000 | 16,384 | | GPT-OSS 20B (OllamaCloud) | `ollamacloud/gpt-oss:20b` | 32,768 | 28,000 | 16,384 | | DeepSeek V4 Flash (OllamaCloud) | `ollamacloud/deepseek-v4-flash` | 65,536 | 56,000 | 16,384 | | Gemini 3 Flash Preview (OllamaCloud) | `ollamacloud/gemini-3-flash-preview` | 1,000,000 | 850,000 | 32,768 | | GLM-5 Turbo | `glm/glm-5-turbo` | 131,072 | 112,000 | 16,384 | | GLM-4.7 Flash | `glm/glm-4.7-flash` | 131,072 | 112,000 | 16,384 | | Mistral Large Latest | `mistral/mistral-large-latest` | 262,144 | 220,000 | 16,384 | > **Compaction formula:** `effective_window = model_context_window - min(tool_output_token_limit, 20000)`. Values above 20k do not change the compaction trigger. > **Rule of thumb:** set `model_auto_compact_token_limit` to 85–88% of `model_context_window`. Never go above 90% — silently ignored. --- ## Model prefix: `cx/` All Codex models in OmniRoute use the `cx/` prefix: | Codex CLI name | OmniRoute model | | ----------------------- | ------------------ | | `cx/gpt-5.5` | GPT-5.5 standard | | `cx/gpt-5.4` | GPT-5.4 standard | | `cx/gpt-5.4-mini` | GPT-5.4 mini | | `cx/gpt-5.1-codex-mini` | GPT-5.1 Codex mini | Other providers use their own prefix (`kmc/`, `glm/`, `ds/`, `ollamacloud/`, `opencode-go/`, `mistral/`) — the prefix matches the OmniRoute provider alias. --- ## Reasoning Effort Controls how much the model "thinks" before responding. | Value | Use for | | -------- | --------------------------------------------- | | `none` | No reasoning — direct response | | `low` | Trivial tasks (rename, format) | | `medium` | **Server default** when not specified | | `high` | Intermediate tasks (refactoring, debug) | | `xhigh` | Architecture, deep analysis, complex problems | ```bash # Per invocation override codex -c model_reasoning_effort=low "rename variable x to count" codex -c model_reasoning_effort=xhigh "design the auth module" ``` Also set a reasoning **summary** so Desktop can render thinking text (not only encrypted blobs): ```toml # ~/.codex/config.toml model_reasoning_effort = "xhigh" # or ultra when supported model_reasoning_summary = "detailed" # auto | concise | detailed | none ``` ### OmniRoute Thinking Budget (server setting) On the OmniRoute host, **Settings → AI → Thinking Budget** must be **`passthrough`** for Codex effort/summary to reach upstream. Mode **`auto` strips** all client `reasoning` / `reasoning_effort` fields and will empty thinking panels even when Codex is configured correctly. Full guide: [THINKING_BUDGET.md](./THINKING_BUDGET.md). Compression and prompt cache are independent and keep working under `passthrough`. --- ## Profiles — named configurations per model/workflow Profiles let you switch model + context window with a single flag. Each profile is a flat `~/.codex/.config.toml` that overlays on top of the base `config.toml`. > **Naming rule (Codex CLI v0.137+):** file must be `~/.codex/.config.toml` — **no `profile-` prefix**. > The CLI resolves `-p kimi-k27` → `~/.codex/kimi-k27.config.toml`. If the file is not found, the default applies silently. ```bash codex --profile kimi-k27 "analyze 10k lines of this codebase" codex -p glm52 "architecture review" codex --profile deepseek-flash "rename variable" # fast, cheap ``` ### Effort profiles (same model, different effort) ```bash codex -p low # cx/gpt-5.5, effort=low codex -p medium # cx/gpt-5.5, effort=medium codex -p high # cx/gpt-5.5, effort=high codex -p xhigh # cx/gpt-5.5, effort=xhigh (default) codex -p chat # cx/gpt-5.5, no effort set (server default) ``` ### Thinking models (alto pensamento) — xhigh + detailed summary | Profile | Model | Context | Use for | | ------------ | --------------------------- | ------- | ---------------------------- | | `kimi-k27` | `kmc/kimi-k2.7` | 128k | Best thinking quality (Kimi) | | `glm52` | `glm/glm-5.2` | 128k | GLM thinking | | `glm52max` | `glm/glm-5.2-max` | 128k | GLM thinking max | | `mimo-pro` | `opencode-go/mimo-v2.5-pro` | 128k | MiMo thinking | | `qwen37plus` | `opencode-go/qwen3.7-plus` | 32k | Qwen thinking | ### Good models (bons) — high effort | Profile | Model | Context | Use for | | -------------- | ----------------------------- | ------- | --------------------------------- | | `kimi-k26` | `kmc/kimi-k2.6` | 128k | General purpose (Kimi) | | `deepseek-pro` | `ollamacloud/deepseek-v4-pro` | 128k | DeepSeek Pro via OllamaCloud | | `deepseek` | `ds/deepseek-v4-pro` | 1M | DeepSeek Pro direct, huge context | | `mimo` | `opencode-go/mimo-v2.5` | 128k | MiMo general | ### Simple models (simples) — no reasoning effort | Profile | Model | Context | Use for | | ---------- | ------------------------------ | ------- | ----------------------- | | `gemma4` | `ollamacloud/gemma4:31b` | 32k | Cost-effective, capable | | `nemotron` | `ollamacloud/nemotron-3-super` | 32k | NVIDIA Nemotron | | `gptoss` | `ollamacloud/gpt-oss:20b` | 32k | Open-source GPT | ### Fast models — low effort | Profile | Model | Context | Use for | | ---------------- | ------------------------------------ | ------- | ----------------------- | | `deepseek-flash` | `ollamacloud/deepseek-v4-flash` | 64k | Quick tasks | | `gemini-flash` | `ollamacloud/gemini-3-flash-preview` | 1M | Very fast, huge context | | `glm5turbo` | `glm/glm-5-turbo` | 128k | GLM Turbo | | `glm47flash` | `glm/glm-4.7-flash` | 128k | GLM Flash | | `mistral` | `mistral/mistral-large-latest` | 256k | Mistral Large | ### Quick decision table | Task | Recommended profile | | -------------------------------- | ------------------------------------------------ | | Rename, format, boilerplate | `--profile deepseek-flash` or `-p low` | | Explain, light review | `-p chat` or `-p gemini-flash` | | Debug, moderate refactor | `-p medium` or `-p kimi-k26` | | New feature, complex tests | `-p high` or `-p mimo` | | Architecture, deep analysis | `-p kimi-k27` or `-p glm52` or `-p xhigh` | | Codebase analysis (needs 1M ctx) | `--profile deepseek` or `--profile gemini-flash` | | Maximum thinking quality | `-p glm52max` or `-p mimo-pro` | | Cost-conscious | `-p gemma4` or `-p gptoss` | --- ## Generating profiles automatically with `omniroute setup-codex` If you run OmniRoute on a VPS, you can auto-generate profile files from the live model catalog: ```bash # From a VPS (uses local OmniRoute on port 20128) omniroute setup-codex # From any machine — point at your VPS omniroute setup-codex --remote http://100.x.x.x:20128 --api-key sk-xxx # Preview without writing files omniroute setup-codex --remote http://100.x.x.x:20128 --dry-run # Only generate GLM and Kimi profiles omniroute setup-codex --only glm,kimi # Write to a custom directory omniroute setup-codex --codex-home /path/to/.codex ``` The command fetches `/v1/models`, uses tuned profiles for known models, falls back to catalog metadata for other compatible text models, and writes `~/.codex/.config.toml` for each. Idempotent — safe to re-run. OmniRoute can also **auto-sync** these same profile files after a successful provider model discovery/import changes the live catalog. This is **opt-in and off by default**: toggle it from the **CLI Code dashboard** ("CLI profile auto-sync" → Codex), or set `OMNIROUTE_AUTO_SYNC_CODEX_PROFILES=true` (it also honors `CLI_ALLOW_CONFIG_WRITES`, on by default). When enabled it only writes separate `~/.codex/*.config.toml` profile files; it never changes the active/default `~/.codex/config.toml`, Codex-lb settings, auth, or provider selection. --- ## Launching Codex with `omniroute launch-codex` Health-checks your OmniRoute instance before launching Codex: ```bash # Launch against local OmniRoute (default port 20128) omniroute launch-codex # Launch with a specific profile omniroute launch-codex --profile kimi-k27 # Launch against a remote VPS omniroute launch-codex --remote http://100.x.x.x:20128/v1 --api-key sk-xxx # Pass extra args to codex omniroute launch-codex --profile glm52 -- --yolo "fix this bug" ``` Codex is also a target of the two generic manifest-driven entry points (`bin/cli/cli-manifest.mjs`): ```bash # Interactive model picker → writes ~/.codex/.config.toml (TOML, env_key) omniroute configure codex # Launch codex with the omniroute provider injected via -c flags (no config written) omniroute run codex ``` --- ## New Codex CLI features (v0.138–v0.141) | Version | Feature | | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | v0.138 | Desktop app handoff (`/app`), v2 personal access tokens, `--profile` as the exclusive profile selector (legacy in-file `[profiles]` tables crash on startup) | | v0.139 | `web_search = "live"` — native web search from code mode; `oneOf`/`allOf` in MCP tool schemas; `codex doctor` env diagnostics | | v0.140 | `/usage` token view in-session; `/import` from Claude Code sessions; `codex delete ` subcommand; Amazon Bedrock auth via `aws` object in provider config | | v0.141 | E2E encrypted Noise relay for remote executors; SQLite WAL fix; P-521 TLS support | ### New `config.toml` fields (post-v0.137) ```toml # Native web search (v0.139) web_search = "live" # "disabled" | "cached" | "live" # Separate developer system prompt (v0.138) developer_instructions = "Always prefer functional style." # Custom compaction prompt compact_prompt = "Summarise the above as bullet points." # Route /review to a cheaper model review_model = "glm/glm-5-turbo" # OpenAI service tier service_tier = "fast" # "fast" | "flex" ``` ### New `[model_providers.]` fields ```toml [model_providers.omniroute] base_url = "http://100.x.x.x:20128/v1" env_key = "OMNIROUTE_API_KEY" requires_openai_auth = false # Static extra headers on every request [model_providers.omniroute.http_headers] "X-Custom-Header" = "value" # Headers read from env vars [model_providers.omniroute.env_http_headers] "X-Trace-Id" = "TRACE_ID" # Extra URL query params (useful for Azure api-version) [model_providers.omniroute.query_params] "api-version" = "2024-12-01-preview" ``` ### Amazon Bedrock auth (v0.140) ```toml [model_providers.bedrock] base_url = "https://bedrock-runtime.us-east-1.amazonaws.com" [model_providers.bedrock.aws] profile = "default" # ~/.aws/credentials profile region = "us-east-1" ``` --- ## Multiple servers ```toml [model_providers.omniroute-main] base_url = "http://192.168.0.1:20128/v1" env_key = "OMNIROUTE_API_KEY" [model_providers.omniroute-tailscale] base_url = "http://100.x.x.x:20128/v1" env_key = "OMNIROUTE_API_KEY" ``` --- ## Claude Code — equivalent configuration | Codex CLI (`config.toml`) | Claude Code (env var) | Effect | | --------------------------------- | ------------------------------------- | ----------------------- | | `tool_output_token_limit = 32768` | _(not directly exposed)_ | Per-tool history cap | | `model_context_window = 400000` | _(determined by the model)_ | Context window | | — | `CLAUDE_CODE_MAX_OUTPUT_TOKENS=65536` | Max tokens per response | ```bash # ~/.bashrc — Claude Code token cap export CLAUDE_CODE_MAX_OUTPUT_TOKENS=65536 ``` --- ## Quick reference — CLI flags | Flag | Short | Effect | | --------------------- | ----- | -------------------------------------------- | | `--model ` | `-m` | Overrides `model` for this invocation | | `--profile ` | `-p` | Loads `~/.codex/.config.toml` | | `--config key=value` | `-c` | Overrides any config.toml field (repeatable) | | `--enable ` | — | Force-enables a feature flag | | `--disable ` | — | Force-disables a feature flag | | `--search` | — | Enable live web search for this invocation | New in v0.140: ```bash codex delete # delete a session codex delete --force # skip confirmation codex debug models --bundled # list bundled model catalog as JSON ``` Inside an interactive session: | Command | Effect | | --------- | ------------------------------------------- | | `/model` | Opens the model picker | | `/usage` | Shows token usage for this session (v0.140) | | `/app` | Hands off to the desktop app (v0.138) | | `/import` | Import a Claude Code session (v0.140) | | `/help` | Lists all slash commands | --- ## Long-running tasks Two OmniRoute defaults can silently sabotage multi-hour Codex CLI sessions. Neither is a Codex CLI setting — both live on the OmniRoute side. Users migrating a config from upstream proxies that pin accounts and disable idle cutoffs often hit both and conclude OmniRoute “cannot sustain a long session.” | Symptom | Likely cause | Knob | | -------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------ | | Session keeps switching accounts / prompt-cache continuity is lost between turns | Session affinity TTL is `0` (disabled) | `sessionAffinityTtlMs` | | Connection dies mid-reasoning with no client-facing prompt | Stream idle watchdog fired after 10 minutes with no upstream chunk | `STREAM_IDLE_TIMEOUT_MS` | Related discussions: [#7126](https://github.com/diegosouzapw/OmniRoute/discussions/7126) (long task drops), [#5718](https://github.com/diegosouzapw/OmniRoute/discussions/5718) (why affinity defaults off). Tracking: [#7287](https://github.com/diegosouzapw/OmniRoute/issues/7287). ### 1. Session affinity — pin one conversation to one account **Default:** `sessionAffinityTtlMs = 0` (disabled). **Where to set it** - Dashboard → **Settings → Routing** → **Session affinity** → **Affinity TTL (seconds)** (`ComboDefaultsTab`) - Or PATCH settings with `sessionAffinityTtlMs` in **milliseconds** (Zod range `0`–`86_400_000`, i.e. up to 24 hours) > Renamed in #7274 from the Codex-only `codexSessionAffinityTtlMs`. The legacy key is still accepted as a read-only alias; new configs should use `sessionAffinityTtlMs`. Affinity now applies to **any** provider once the TTL is above `0`, not only Codex — see [`docs/architecture/RESILIENCE_GUIDE.md`](../architecture/RESILIENCE_GUIDE.md) → Session affinity. **What breaks when it stays at 0** Every turn of a multi-turn Codex conversation is routed independently by the active combo strategy and can land on a **different account per turn**. That breaks upstream session / prompt-cache continuity. OmniRoute only consults Codex session headers (`x-codex-session-id` / `x-session-id` / `x-omniroute-session`) and body fields such as `prompt_cache_key` / `session_id` when the TTL is greater than `0` (`extractSessionAffinityKey` in `src/sse/services/auth.ts`). **Recommended for a multi-hour single task** Set the TTL **above the expected wall-clock length of the task** (UI max is **86400 seconds** = 24 hours): | Expected task length | Affinity TTL (UI, seconds) | `sessionAffinityTtlMs` | | -------------------- | -------------------------- | ---------------------- | | A few hours | `14400` (4h) | `14400000` | | Overnight / ~12h | `43200` (12h) | `43200000` | | Full day | `86400` (24h, maximum) | `86400000` | Opt-in is deliberate: disabling affinity favors load-balancing across accounts; enabling it favors continuity for one long agent session. This guide does **not** change the default — operators running long Codex tasks must opt in. ### 2. Stream idle timeout — do not kill quiet reasoning turns **Default:** `STREAM_IDLE_TIMEOUT_MS = 600000` (10 minutes). It inherits from `REQUEST_TIMEOUT_MS` when unset; the shared baseline is also 600000. See [`docs/guides/SETUP_GUIDE.md`](SETUP_GUIDE.md) → Timeouts. **What breaks at the default** A Codex reasoning / tool turn that stays silent for more than 10 minutes with **no real upstream chunk** is force-closed by the SSE idle watchdog (`open-sse/utils/stream.ts`). The client often sees a bare connection drop — matching “stopped automatically without any notification.” Critical detail: OmniRoute’s synthetic SSE **heartbeat does not reset** the idle clock. Only a real upstream body chunk updates `lastChunkTime`. A quiet model that is still “thinking” looks identical to a stalled upstream from the watchdog’s point of view. Related Undici body inactivity: `FETCH_BODY_TIMEOUT_MS` (also defaults to the same 10-minute baseline; `0` disables it). For streaming, `FETCH_TIMEOUT_MS` only covers connection setup / first headers — once the stream is active, stalls are governed by `STREAM_IDLE_TIMEOUT_MS` and `FETCH_BODY_TIMEOUT_MS`. **Recommended for a multi-hour single task** In the OmniRoute process environment (`.env` / compose / systemd): ```bash # Disable stream idle + body inactivity cutoffs for long reasoning turns STREAM_IDLE_TIMEOUT_MS=0 FETCH_BODY_TIMEOUT_MS=0 ``` Or raise them above the longest quiet gap you expect (values are milliseconds): ```bash # Example: allow up to 2 hours of silence between upstream chunks STREAM_IDLE_TIMEOUT_MS=7200000 FETCH_BODY_TIMEOUT_MS=7200000 ``` Restart OmniRoute after changing these env vars. ### Concrete recipe — multi-hour Codex task 1. **Pin the account:** Dashboard → Settings → Routing → Session affinity → Affinity TTL = `43200` (12h) or `86400` (24h max). 2. **Raise / disable idle cutoffs** in OmniRoute’s environment: ```bash STREAM_IDLE_TIMEOUT_MS=0 FETCH_BODY_TIMEOUT_MS=0 ``` 3. Keep the usual Codex `config.toml` (`wire_api = "responses"`, correct `base_url`, `OMNIROUTE_API_KEY`) — no Codex-side affinity/idle knobs exist for these two behaviors. 4. Restart OmniRoute, then start the long Codex task. ### Defaults decision (#7287) | Knob | Ship default | Change in this guide? | | ------------------------ | ----------------- | ----------------------------------------------------------------------------------------- | | `sessionAffinityTtlMs` | `0` (off) | **No** — remains opt-in (load-balancing vs continuity; see Discussion #5718) | | `STREAM_IDLE_TIMEOUT_MS` | `600000` (10 min) | **No** — remains 10 minutes for general traffic; long Codex operators raise or disable it | Flipping either default globally would change behavior for every client of an instance, not only Codex. Document the knobs; leave the defaults alone until an explicit operator decision says otherwise. ### Diagnosing idle cuts When the idle watchdog fires, OmniRoute logs a line shaped like: ```text [STREAM] Idle timeout: no data from codex for 600000ms (model: cx/gpt-5.5) ``` Grep for `Idle timeout: no data from` (or the code `stream_idle_timeout` / error name `StreamIdleTimeoutError`). The provider segment is whatever OmniRoute used for that request (`codex`, another provider id, or `provider` if unknown) — it is not always the literal string `codex`. --- ## Troubleshooting **`Error: wire_api = "chat" is no longer supported`** Remove `wire_api = "chat"` from your config. Set `wire_api = "responses"` or omit the field (defaults to `"responses"` since v0.138). **`Error: model not found`** Verify the model exists in OmniRoute with the correct prefix. Use `omniroute models list` or open `/dashboard/providers/`. **`Authentication error`** Confirm `OMNIROUTE_API_KEY` is exported: `echo $OMNIROUTE_API_KEY`. **`ERROR: Missing environment variable: OMNIROUTE_API_KEY`** Codex validates that the env var exists before making the first request. Export a real key for protected servers, or a non-empty placeholder such as `OMNIROUTE_API_KEY=local` when your **local** OmniRoute instance does not require auth. Restart the shell if you added it to `~/.bashrc` or `~/.zshrc`. **`Connection refused`** Verify OmniRoute is running and the `base_url` host/port is correct for your network (local vs Tailscale vs VPS). **Session crashes near context limit** Set `model_context_window` and `model_auto_compact_token_limit` explicitly. See the context window table above. **Compaction fires too late** Lower `model_auto_compact_token_limit` to 80–85% of the window. Never set above 90%. **Profile not loading (`-p ` silently ignored)** Confirm the file exists at `~/.codex/.config.toml` (no `profile-` prefix). Run `ls ~/.codex/*.config.toml`. **Long Codex task drops mid-run / switches accounts between turns** See [Long-running tasks](#long-running-tasks). Enable session affinity (TTL above task length) and raise or disable `STREAM_IDLE_TIMEOUT_MS` / `FETCH_BODY_TIMEOUT_MS`. Grep OmniRoute logs for `Idle timeout: no data from`.