Skip to content

[bug] code-mode exec catalog rejects routed default.update_goal as undeclared #5495

Description

@yxr1995-maker

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:

  1. When the request catalog is genuine code mode (exec declared, no top-level goal tools declared), provider-emitted update_goal, default.update_goal (and sibling create_goal / get_goal) is not rejected as an undeclared client tool.
  2. The call is restored as the declared exec custom 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.

  1. When the catalog declares the bare goal tool, keep [bug] routed provider prefixes bare Codex tool with default.: default.view_image rejected as undeclared #4176 / fix(responses): normalize invented default. namespace back to the declared bare tool #4264 behaviour: an invented default.update_goal rewrites to bare update_goal and is not compiled into exec.
  2. When neither exec nor a goal tool is declared, keep fail-closed.

Minimal redacted request or reproduction

  1. Codex Desktop in code mode: request catalog declares top-level exec (and non-helper interaction tools such as wait, request_user_input). Goal tools exist only as await tools.update_goal(...) inside exec.
  2. Route the turn to a model that emits goal tool calls directly (observed with opencode-go/muse-spark-1.3-contributor).
  3. Upstream emits a top-level function_call with name: "default.update_goal" and arguments: {"status":"complete"}.
  4. normalizeDeclaredToolName strips the invented default. prefix only when the stripped name is in CODE_MODE_HELPER_TOOL_NAMES. Because update_goal is 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

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:

  1. src/types/tools.ts: add update_goal, create_goal, get_goal to CODE_MODE_HELPER_TOOL_NAMES.
  2. src/responses/code-mode-helper-compat.ts: add explicit compile branches in compileCodeModeHelperInput for the three goal helpers to emit const result = await tools.<helper>(<args>);\ntext(result);. Without these branches, unhandled helpers fall back to tools.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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    catalogModel catalog, slugs, visibility, routed entriesproviderProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportstoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions