Repository navigation
[bug] code-mode exec catalog rejects routed default.update_goal as undeclared #5495
Description
Client or integration
Codex App (Desktop)
Provider or upstream service
opencode-go (muse-spark-1.3-contributor; also applies to any routed model that emits OpenCode-native default.* tool names)
OpenCodex version
@bitkyc08/opencodex 2.59.0. Checked on 2026-09-22 via GitHub API against main and tag v2.60.0: CODE_MODE_HELPER_TOOL_NAMES still lists only exec_command, shell_command, write_stdin, apply_patch, view_image.
Endpoint or capability
/v1/responses streaming tool calls, Codex code mode (exec as the only top-level client tool)
Current behaviour
A Codex Desktop code-mode turn against a routed model is killed mid-stream with:
stream disconnected before completion: routed provider emitted undeclared client tool "default.update_goal"; only request-declared tools may be called
The message is produced by undeclaredToolCallMessage in src/server/responses-undeclared-tool-guard.ts. The proxy usage log records HTTP status 200 while the client rollout records the stream termination error.
Observed request: provider opencode-go, model muse-spark-1.3-contributor, inbound responses, timestamp 2026-09-22 14:00:24 (Asia/Shanghai). Upstream emitted a top-level tool call named default.update_goal instead of the code-mode nested helper, and the guard failed the stream closed.
Expected behaviour
Same contract as the code-mode helper remap for apply_patch / write_stdin / view_image / exec_command, and the precedent #4412 / #4475 established for view_image:
- When the request catalog is genuine code mode (
execdeclared, no top-level goal tools declared), provider-emittedupdate_goal,default.update_goal(and siblingcreate_goal/get_goal) is not rejected as an undeclared client tool. - The call is restored as the declared
execcustom tool, with the body compiled to the nested helper Codex runs:
const result = await tools.update_goal({"status":"complete"});
text(result);The Codex code-mode exec tool description advertises tools.create_goal(args), tools.get_goal(args), and tools.update_goal(args) alongside other nested helpers; these are not wire tools.
- When the catalog declares the bare goal tool, keep [bug] routed provider prefixes bare Codex tool with
default.:default.view_imagerejected as undeclared #4176 / fix(responses): normalize invented default. namespace back to the declared bare tool #4264 behaviour: an inventeddefault.update_goalrewrites to bareupdate_goaland is not compiled intoexec. - When neither
execnor a goal tool is declared, keep fail-closed.
Minimal redacted request or reproduction
- Codex Desktop in code mode: request catalog declares top-level
exec(and non-helper interaction tools such aswait,request_user_input). Goal tools exist only asawait tools.update_goal(...)insideexec. - Route the turn to a model that emits goal tool calls directly (observed with
opencode-go/muse-spark-1.3-contributor). - Upstream emits a top-level
function_callwithname: "default.update_goal"andarguments: {"status":"complete"}. normalizeDeclaredToolNamestrips the inventeddefault.prefix only when the stripped name is inCODE_MODE_HELPER_TOOL_NAMES. Becauseupdate_goalis absent from that set, the guard flags the call as undeclared and terminates the stream.
Actual response or error
stream disconnected before completion: routed provider emitted undeclared client tool "default.update_goal"; only request-declared tools may be called
Upstream documentation
- Codex code-mode nested helpers (from the request
exectool description):tools.create_goal,tools.get_goal,tools.update_goal. - OpenCodex fail-closed undeclared-tool guard:
src/server/responses-undeclared-tool-guard.ts. - Precedent: [bug] code-mode exec catalog rejects routed default.view_image as undeclared #4412 (closed) and merged PR fix(responses): execute code-mode view_image through unified exec #4475 for
default.view_image. Thedefault.fold branch innormalizeDeclaredToolName(commented "Code mode never declares bare helper names") is present in 2.59.0; the three goal helper names are missing fromCODE_MODE_HELPER_TOOL_NAMES. - Invented
default.prefix when the bare tool is declared: [bug] routed provider prefixes bare Codex tool withdefault.:default.view_imagerejected as undeclared #4176, fixed by fix(responses): normalize invented default. namespace back to the declared bare tool #4264.
Suggested mapping or implementation notes
| Catalog | Provider emits | Today | Should become |
|---|---|---|---|
{ exec } |
update_goal |
stream aborted, undeclared | exec + tools.update_goal(...) |
{ exec } |
default.update_goal |
stream aborted, undeclared | exec + tools.update_goal(...) |
{ update_goal } |
default.update_goal |
accepted (#4176 / #4264) | unchanged (update_goal) |
{} |
default.update_goal |
aborted | aborted |
Concrete two-part change:
src/types/tools.ts: addupdate_goal,create_goal,get_goaltoCODE_MODE_HELPER_TOOL_NAMES.src/responses/code-mode-helper-compat.ts: add explicit compile branches incompileCodeModeHelperInputfor the three goal helpers to emitconst result = await tools.<helper>(<args>);\ntext(result);. Without these branches, unhandled helpers fall back totools.exec_command(...).
Relevant test files: tests/responses/responses-undeclared-tool-guard.test.ts, tests/responses/legacy-shell-compat.test.ts, and tests/responses/responses-code-mode-patch-compile.test.ts.
Additional context
Verified locally on 2.59.0 (macOS, Codex App, opencode-go): applying both parts passes an 18-assertion test covering normalization to exec, guard acceptance, compilation to tools.update_goal(...) instead of tools.exec_command(...), and restoring the item as a custom_tool_call named exec. Undeclared tools remain rejected, and declared bare tools retain precedence.
Uncertainty: the wire trace contained literal default.update_goal in the name field. Providers sending a structured namespace field (namespace: "default", name: "update_goal") have not been observed in this setup and remain unverified; under current code, routedCustomToolTargetName returns undefined when namespace is a string.
Related: #4412 / #4475 (view_image, closed/merged), #4176 / #4264 (bare-name fold), #3402 / #3403 (dotted names).
Activity
Ingwannu commentedon Sep 23, 2026
Confirmed on current
dev: the code-mode helper allowlist still omittedcreate_goal,get_goal, andupdate_goal, and the compiler fallback would incorrectly turn an admitted goal helper intotools.exec_command(...). I opened draft PR #5659 with a bounded fix and regression coverage. It preserves bare-tool precedence and still rejects the call when neither the bare goal tool nor genuine code-modeexecis declared. Focused tests are green; exact-head CI and @lidge-jun review are pending before merge.fix(responses): restore code-mode goal helpers
fix(responses): bundle lane E — combo resend safety, WebSocket replac…
lidge-jun commentedon Sep 23, 2026
Fixed on
devby #5675 (squash-merged as aed3bb8, carrying #5659): code-mode exec catalogs accept the routed goal helpers again, sodefault.update_goaland its siblings are no longer rejected as undeclared.Merge upstream/main (v2.67.0) into codex/shadow-call-per-source-modelmap