Bug: ToolName namespace collision causes 'unsupported custom tool call: execexec' with gpt-5.6-sol (HIGH-PRIORITY)
Effect: The GPT 5.6 models are unusable as they are having issues with tool calling.
Problem
Models using use_responses_lite (e.g. gpt-5.6-sol) emit ResponseItem::CustomToolCall events where the namespace field equals the tool name:
{ "name": "exec", "namespace": "exec", "call_id": "call_abc123", "input": "..." }
The built-in exec handler is registered under ToolName::plain("exec") (namespace: None). When the router constructs a ToolName from the response, it creates ToolName { name: "exec", namespace: Some("exec") }, which doesn't match. The registry lookup fails and the Display impl concatenates namespace + name:
unsupported custom tool call: execexec
This kills the tool execution bridge for the entire session.
Example GPT 5.6 Sol (Ultra) responses:
- I hit a tooling hiccup on my first read command. Retrying with the shell directly so I can keep moving on the repo
inspection.`
- I’m checking the tool surface itself for direct shell access. The collaboration tools are fine; the shell wrapper is
the piece misbehaving, so I’m trying the underlying command entrypoint directly.`
And then it just fails to do anything, ofcourse.
---
Root Cause
ToolName::new(), ToolName::namespaced(), and the derived Deserialize impl preserve the namespace field verbatim from the wire format. There is no normalization to strip a redundant namespace that equals the tool name.
---
Fix (ready to merge)
Affects: codex-rs/protocol/src/tool_name.rs, codex-rs/core/src/tools/router.rs
Normalize ToolName at construction time — when namespace == name, set namespace to None:
| Entry Point | Change |
|---|---|
| ToolName::new() | .filter(\|ns\| ns != &name) |
| ToolName::namespaced() | if namespace == name { None } else { Some(namespace) } |
| Deserialize impl | Replace derived with manual impl delegating to ToolName::new() |
The fix is implemented and tested in my fork:
➡️ Branch: MayukXT/codex:fix/tool-namespace-collision-regression
➡️ Diff: main...MayukXT:fix/tool-namespace-collision-regression
---
Test Results
Unit tests (codex-protocol) — test_tool_name_normalization:
- Covers plain, distinct-namespace, same-namespace construction, and JSON deserialization
- All 241 protocol tests pass ✅
Integration test (codex-core) — build_custom_tool_call_normalizes_equal_namespace_and_name:
- Feeds
CustomToolCall { namespace: Some("exec"), name: "exec" }throughToolRouter::build_tool_call() - Asserts output is
ToolName::plain("exec") - All 328 core tool tests pass ✅
E2E:
- Built CLI from source, ran
codex exec "..."withgpt-5.6-sol - Model emitted
exec/execcustom tool calls — fix normalized them, router dispatched correctly, no error ✅
---
Backward Compatibility
Serializeis untouched — wire format output unchanged- Only
namespace == namecollapses toNone; MCP tools likemcp__python/execare unaffected ToolName::plain()bypasses the filter entirely
---
Fix authored by @MayukXT
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗