Windows: custom provider sessions expose only image_gen (dynamic_tool_count=0) — 0.147.0 regression

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

What version of Codex are you using?

  • Codex Desktop (Microsoft Store) 26.803.5235.0, bundled CLI 0.147.0-alpha.6.5
  • Also reproduced with npm CLI stable 0.147.0 and 0.148.0-alpha.5 (all broken)
  • Last working combination on this machine: 0.147.0-alpha.1.2 (desktop sessions, direct shell_command tools)

What subscription do you have?

Custom OpenAI-compatible provider through a local proxy (cc-switch, base_url = http://127.0.0.1:15721/v1, requires_openai_auth = true, experimental_bearer_token = "PROXY_MANAGED"). Auth mode reported by CLI: ApiKey. No ChatGPT OAuth login. The proxy forwards to a third-party Responses-compatible endpoint and is verified to pass tools through correctly (direct curl to the proxy with a tools array returns a proper function_call).

Which model were you using?

gpt-5.6-terra / gpt-5.6-luna / gpt-5.6-sol (custom models via the proxy, wire_api = "responses"). Switching models does not change the symptom.

What platform is your computer?

Windows 11, x64.

What issue are you seeing?

In every session (desktop app and CLI), the model is exposed only one tool: image_gen. No shell, filesystem, browser, or web tools are registered. The agent reports: "no shell, browser, filesystem, or external-service tool is exposed to me in this conversation."

Evidence from the session log (RUST_LOG=debug, thread/start):

thread_start.dynamic_tool_count=0

A captured request shows the tools array contains only:

exec, wait, request_user_input, collaboration (spawn_agent, followup_task, send_message, ...)

No nested tools (no exec_command, no fs tools) are described inside exec. First-turn input_tokens is ~10-11K; working sessions (0.147.0-alpha.1.2) were ~19-47K (tools present).

Steps to reproduce

  1. Fresh CODEX_HOME with a minimal config (only model_provider.custom + base_url + token, no plugins, no marketplaces) — still reproduces. So plugins/marketplaces config is not the trigger.
  2. Run codex exec --json "What tools do you have available?" → only image_gen.
  3. Also reproduced via the desktop app sessions (originator=Codex Desktop).

What I already ruled out

  • Proxy / network layer: direct POST /v1/responses to the same base_url with a tools array returns a valid function_call → backend and proxy fully support tools.
  • Model: same model called directly through the proxy does tool calls; switching models (terra/luna/sol) changes nothing.
  • Sandbox / permissions: danger-full-access, approval never; <permissions instructions> present in session.
  • Runtime cache: renaming ~/.cache/codex-runtimes/codex-primary-runtime changes nothing (CLI doesn't even re-download it) → not a stale-runtime issue.
  • Config state: brand-new minimal CODEX_HOME reproduces.
  • Provider switch: switching cc-switch providers (different upstream, different model names) still reproduces.

Regression timeline (same machine, same config)

  • 0.146.0-alpha.9.2 (Aug 1-2): shell_command works — 1195 tool calls in one session
  • 0.147.0-alpha.1.2 (Aug 6 09:21): shell_command works — 10 calls
  • 0.147.0-alpha.6.5 (Aug 9): only image_gen, dynamic_tool_count=0
  • npm 0.147.0 stable & 0.148.0-alpha.5: same broken behavior

Related issues

  • #37075 Codex Desktop omits codex_app project/thread tools with a custom Responses provider
  • #37376 Regression in 0.147.0: upgrade leaves all tools unavailable with existing code_mode_host=false
  • #31750 Browser & Computer Use plugins silently unusable with a custom model_provider (no tool_search / dynamic)
  • #31710 exec_command intermittently unsupported, then tool disappears entirely

View original on GitHub ↗

6 Comments

github-actions[bot] contributor · 18 days ago

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

  • #37410

Powered by Codex Action

lisu108 · 11 days ago

Same regression on Windows with a custom Responses provider.

  • Broken: npm CLI 0.147.0 (and Codex Desktop on 0.147)
  • Works: npm CLI 0.146.0, same config.toml, same proxy, same model gpt-5.6-terra
  • Provider: wire_api = "responses", requires_openai_auth = false
  • 0.147 tool surface here: only request_user_input + wait (plus collaboration tools when multi_agent is on). No exec / apply_patch.
  • Also failed after codex features disable multi_agent and codex exec --enable unified_exec
  • Router logs: request_user_input is unavailable in Default mode; wait calls fail with invalid type: floating point ... expected u64

So this looks like the same client-side dynamic tool injection failure (dynamic_tool_count=0 / missing local tools), not the proxy. Official model_provider=openai was unreachable from this network (WS/HTTPS timeout), so I could not A/B that path.

seekskyworld · 11 days ago

I traced this regression to #37022 / commit f21dc463, which changed Responses Lite serialization so top-level function and custom tools are coalesced into the default functions namespace.

For the affected custom Responses provider, the request shape changes from a top-level freeform tool:

{"type":"custom","name":"exec"}

to a custom tool nested inside a namespace:

{"type":"namespace","name":"functions","tools":[{"type":"custom","name":"exec"}]}

The provider accepts namespace/function structures but drops nested custom/freeform tools. With CodeModeOnly, exec is the only shell/file execution surface, so the model has no usable local tools and produces no exec calls. The two affected 0.147.0 rollout sessions both used a custom provider, Responses Lite, gpt-5.6-sol, and CodeModeOnly; both recorded zero exec calls. The same setup works on 0.146.1, before the serialization change.

This is distinct from #37376: that issue is the intentional fail-closed path when code_mode_host = false; here the host and local environment are available, but the tool is lost in the outbound request shape.

I prepared a focused fix that gates only the Responses Lite default-tool namespace transformation by provider capability. OpenAI, Azure Responses, and Amazon Bedrock keep the current namespaced shape; generic custom providers keep default function/custom tools top-level. Explicit namespaces remain supported.

Patch: https://github.com/seekskyworld/codex/commit/5949ab7c6aba2d60620867a92b16ecd9495088bb
Branch: https://github.com/seekskyworld/codex/tree/fix/custom-provider-responses-lite-tools

Validation: model-provider tests 65/65; Responses Lite core tests 19/19; the new request-level regression test fails before the fix and passes after it. The branch also passes scoped Clippy fixes, formatting, and staged diff checks.

Per the invitation-only contribution policy, could a Codex maintainer confirm the approach and invite this PR? GitHub currently rejects PR creation from this fork with CreatePullRequest permission denied.

jdcodes1 · 9 days ago

Narrowing from main @ 1f41cc5d92. Two verified facts and a discriminating test:

  1. There is a filter that drops whole tool namespaces per provider — merge_into_namespaces(...).filter(|spec| namespace_tools_enabled(turn_context) || !Namespace) (core/src/tools/spec_plan.rs#L516-L521) — but ConfiguredModelProvider::capabilities() leaves namespace_tools: true even for custom providers (model-provider/src/provider.rs#L294-L310), so a plain [model_providers.custom] config shouldn't trip it.
  2. What actually shapes the tool set is the resolved ModelInfo for your custom model slug: get_model_info longest-prefix-matches the catalog and, on a miss, builds a fallback via model_info_from_slug with used_fallback_model_metadata: true (models-manager/src/manager.rs#L654-L673). Tool mode (code-mode vs classic), shell type, lite mode, and nested-tool exposure all key off that struct — and your regression window (alpha.1.2 → 0.147.0) is exactly where an unknown-slug fallback's defaults changing would flip a session into the shape you captured (only exec/wait/collab + image_gen, no nested tool descriptions).

Discriminator: name your custom model with a catalog-prefix slug (e.g. gpt-5.6-sol pointed at your base_url) so it borrows real catalog metadata. If tools come back, the fallback ModelInfo for unrecognized slugs is the regression, and the fix is in model_info_from_slug's defaults (or honoring -c model-metadata overrides for custom slugs) rather than anywhere in provider config.

zmzwynzj · 9 days ago

That actually makes a lot of sense based on what happened on my side.

Initially, I was using a custom provider with model names matching the
official model names, so I didn't explicitly define each model in the
configuration and assumed Codex would recognize them automatically.

Later, I manually added/configured each model explicitly, and the problem
disappeared. I didn't know why it started working at the time, but your
explanation about the resolved ModelInfo and the fallback for unrecognized
model slugs seems to fit what I experienced very well.

So it's possible that explicitly defining the models had already worked
around the issue on my side.

I hope this information helps with narrowing down the cause. Thanks again
for investigating it!

Joey Dafforn @.***> 于 2026年8月18日周二 下午10:11写道:

jdcodes1 left a comment (openai/codex#37718) <https://github.com/openai/codex/issues/37718#issuecomment-5329396722> Narrowing from main @ 1f41cc5 <https://github.com/openai/codex/commit/1f41cc5d92722748e45cae9cecc6d883a4e7cbb1>. Two verified facts and a discriminating test: 1. There is a filter that drops whole tool namespaces per provider — merge_into_namespaces(...).filter(|spec| namespace_tools_enabled(turn_context) || !Namespace) ( core/src/tools/spec_plan.rs#L516-L521) — but ConfiguredModelProvider::capabilities() leaves namespace_tools: true even for custom providers (model-provider/src/provider.rs#L294-L310), so a plain [model_providers.custom] config shouldn't trip it. 2. What actually shapes the tool set is the resolved ModelInfo for your custom model slug: get_model_info longest-prefix-matches the catalog and, on a miss, builds a fallback via model_info_from_slug with used_fallback_model_metadata: true ( models-manager/src/manager.rs#L654-L673). Tool mode (code-mode vs classic), shell type, lite mode, and nested-tool exposure all key off that struct — and your regression window (alpha.1.2 → 0.147.0) is exactly where an unknown-slug fallback's defaults changing would flip a session into the shape you captured (only exec/wait/collab + image_gen, no nested tool descriptions). Discriminator: name your custom model with a catalog-prefix slug (e.g. gpt-5.6-sol pointed at your base_url) so it borrows real catalog metadata. If tools come back, the fallback ModelInfo for unrecognized slugs is the regression, and the fix is in model_info_from_slug's defaults (or honoring -c model-metadata overrides for custom slugs) rather than anywhere in provider config. — Reply to this email directly, view it on GitHub <https://github.com/openai/codex/issues/37718?email_source=notifications&email_token=BKKBGA537JFDMPUASJIVRL35KRP2LA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMZSHEZTSNRXGIZKM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-5329396722>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/BKKBGA2TO56KG2GB755LAKL5KRP2LAVCNFSNUABFKJSXA33TNF2G64TZHM4TMNJUGE2TMNBZHNEXG43VMU5TKMJQGM4TANZRGEZKC5QC> . Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS <https://github.com/notifications/mobile/ios/BKKBGA47RTXQVVMZLKN2VAD5KRP2LA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMZSHEZTSNRXGIZKM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KUZTPN52GK4S7NFXXG> and Android <https://github.com/notifications/mobile/android/BKKBGA47ZTQSAUCP7AZJR435KRP2LA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMZSHEZTSNRXGIZKM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2K4ZTPN52GK4S7MFXGI4TPNFSA>. Download it today! You are receiving this because you authored the thread.Message ID: @.***>
jdcodes1 · 9 days ago

The captured tools array is itself the diagnostic: exec + wait + collaboration is the Code Mode surface (ToolMode::CodeMode replaces the classic shell/apply_patch tools with a JS exec entrypoint whose nested tools are described dynamically — core/src/tools/mod.rs#L74-L76, finalize_tool_router in spec_plan.rs). So the classic tools weren't dropped randomly — the session entered code mode, and then the second half failed: dynamic_tool_count=0 means the nested catalog (exec_command, fs tools, etc.) that's supposed to be advertised inside exec never populated. The model is left with an exec shell it has no vocabulary for, plus image_gen, matching your ~10K first-turn tokens vs ~19-47K when the full tool descriptions are present.

Both halves point at the managed-features/model-catalog dependency: tool-mode selection and the dynamic nested-tool catalog are driven by per-model managed feature flags that come from the first-party backend. With only model_provider.custom + base_url, 0.147 appears to enable code mode from defaults while the catalog fetch yields nothing — a regression from 0.147.0-alpha.1.2 where you got the classic toolset. Related: #37825 documents the adjacent failure (custom providers rejecting code mode's freeform exec outright), and #37813 shows the same unknown-model fallback stripping capabilities silently.

Actionable: code mode should be gated on the nested catalog actually resolving — if dynamic_tool_count == 0, fall back to the classic shell/apply_patch toolset instead of shipping an unusable exec. Worth capturing RUST_LOG=codex_core=trace around finalize_tool_router to confirm which feature flags the session resolved.