Windows: custom provider sessions expose only image_gen (dynamic_tool_count=0) — 0.147.0 regression
What version of Codex are you using?
- Codex Desktop (Microsoft Store)
26.803.5235.0, bundled CLI0.147.0-alpha.6.5 - Also reproduced with npm CLI stable
0.147.0and0.148.0-alpha.5(all broken) - Last working combination on this machine:
0.147.0-alpha.1.2(desktop sessions, directshell_commandtools)
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
- Fresh
CODEX_HOMEwith a minimal config (onlymodel_provider.custom+base_url+ token, no plugins, no marketplaces) — still reproduces. So plugins/marketplaces config is not the trigger. - Run
codex exec --json "What tools do you have available?"→ onlyimage_gen. - Also reproduced via the desktop app sessions (originator=Codex Desktop).
What I already ruled out
- Proxy / network layer: direct
POST /v1/responsesto the same base_url with atoolsarray returns a validfunction_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, approvalnever;<permissions instructions>present in session. - Runtime cache: renaming
~/.cache/codex-runtimes/codex-primary-runtimechanges nothing (CLI doesn't even re-download it) → not a stale-runtime issue. - Config state: brand-new minimal
CODEX_HOMEreproduces. - 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_commandworks — 1195 tool calls in one session0.147.0-alpha.1.2(Aug 6 09:21):shell_commandworks — 10 calls0.147.0-alpha.6.5(Aug 9): onlyimage_gen,dynamic_tool_count=0- npm
0.147.0stable &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
6 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Same regression on Windows with a custom Responses provider.
gpt-5.6-terrawire_api = "responses",requires_openai_auth = falserequest_user_input+wait(plus collaboration tools whenmulti_agentis on). Noexec/apply_patch.codex features disable multi_agentandcodex exec --enable unified_execrequest_user_input is unavailable in Default mode;waitcalls fail withinvalid type: floating point ... expected u64So this looks like the same client-side dynamic tool injection failure (
dynamic_tool_count=0/ missing local tools), not the proxy. Officialmodel_provider=openaiwas unreachable from this network (WS/HTTPS timeout), so I could not A/B that path.I traced this regression to #37022 / commit
f21dc463, which changed Responses Lite serialization so top-level function and custom tools are coalesced into the defaultfunctionsnamespace.For the affected custom Responses provider, the request shape changes from a top-level freeform tool:
to a custom tool nested inside a namespace:
The provider accepts namespace/function structures but drops nested custom/freeform tools. With
CodeModeOnly,execis 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, andCodeModeOnly; 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
CreatePullRequestpermission denied.Narrowing from
main@ 1f41cc5d92. Two verified facts and a discriminating test:merge_into_namespaces(...).filter(|spec| namespace_tools_enabled(turn_context) || !Namespace)(core/src/tools/spec_plan.rs#L516-L521) — butConfiguredModelProvider::capabilities()leavesnamespace_tools: trueeven for custom providers (model-provider/src/provider.rs#L294-L310), so a plain[model_providers.custom]config shouldn't trip it.ModelInfofor your custom model slug:get_model_infolongest-prefix-matches the catalog and, on a miss, builds a fallback viamodel_info_from_slugwithused_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 (onlyexec/wait/collab +image_gen, no nested tool descriptions).Discriminator: name your custom model with a catalog-prefix slug (e.g.
gpt-5.6-solpointed at yourbase_url) so it borrows real catalog metadata. If tools come back, the fallbackModelInfofor unrecognized slugs is the regression, and the fix is inmodel_info_from_slug's defaults (or honoring-cmodel-metadata overrides for custom slugs) rather than anywhere in provider config.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写道:
The captured tools array is itself the diagnostic:
exec+wait+ collaboration is the Code Mode surface (ToolMode::CodeModereplaces the classic shell/apply_patch tools with a JSexecentrypoint whose nested tools are described dynamically —core/src/tools/mod.rs#L74-L76,finalize_tool_routerinspec_plan.rs). So the classic tools weren't dropped randomly — the session entered code mode, and then the second half failed:dynamic_tool_count=0means the nested catalog (exec_command, fs tools, etc.) that's supposed to be advertised insideexecnever populated. The model is left with an exec shell it has no vocabulary for, plusimage_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 freeformexecoutright), 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 unusableexec. Worth capturingRUST_LOG=codex_core=tracearoundfinalize_tool_routerto confirm which feature flags the session resolved.