Skip to content

[bug] code-mode exec catalog rejects routed default.view_image as undeclared #4412

Description

@samwang0041-star

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.* tools)

OpenCodex version

@bitkyc08/opencodex 2.51.0, and current dev (aa0dd50). The gap is still present on dev after #4264.

Endpoint or capability

/v1/responses tool calls / streaming, 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.view_image"; only request-declared tools may be called

The message is produced by src/bridge.ts / src/server/responses-undeclared-tool-guard.ts. HTTP/SSE then surfaces as response.failed (undeclared_tool_call / upstream_error). The turn is lost.

This happens after the model captures a screenshot (e.g. screencapture … /tmp/hud-after.png via tools.exec_command) and then tries to look at the PNG.

Expected behaviour

Same contract as the existing code-mode helper remap for apply_patch / write_stdin / exec_command:

  1. When the request catalog is genuine code mode (bare exec declared, no top-level view_image), a provider-emitted view_image or invented default.view_image is not an undeclared client tool.
  2. The call is restored as the declared exec custom tool, with the body compiled to the nested helper Codex actually runs:
const result = await tools.view_image({ path: "…", detail: "high" });
if (result && result.image_url) image(result.image_url);

That nested API is real. Codex code-mode exec already exposes tools.view_image({ path, detail? }) and image(image_url); successful host output is a custom_tool_call_output that includes input_image.

  1. When the catalog does declare top-level view_image, 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: invented default.view_image rewrites to bare view_image and is not compiled into exec.
  2. When neither exec nor view_image is declared, keep fail-closed.

Minimal redacted request or reproduction

  1. Codex Desktop, code mode, model opencode-go/muse-spark-1.3-contributor (or any OpenCode-native model that emits default.* tools).
  2. Ask the model to screenshot a window and inspect the PNG.
  3. Observed sequence: many successful exec / tools.exec_command calls, then a top-level tool call named default.view_image.
  4. Stream ends with the error above.

Request catalog for that turn is the code-mode shape: top-level exec (plus MCP tools such as computer-use). view_image is not a wire tool; it only exists as await tools.view_image(...) inside exec.

Actual response or error

routed provider emitted undeclared client tool "default.view_image"; only request-declared tools may be called

Codex wraps it as stream disconnected before completion: ….

Upstream documentation

Suggested mapping or implementation notes

This is a different axis from #4176.

Catalog Provider emits Today Should become
{ view_image } default.view_image accepted after #4264 unchanged (view_image)
{ exec } view_image 502 undeclared exec + tools.view_image(...)
{ exec } default.view_image 502 undeclared exec + tools.view_image(...)
{} default.view_image 502 502

Concrete change (same files as apply_patch / write_stdin):

  1. src/types/tools.ts — add view_image to CODE_MODE_HELPER_TOOL_NAMES. When exec is declared and default.<helper> was not itself declared, strip the invented default. prefix before helper→exec normalization. Otherwise default.view_image never matches the helper list.
  2. src/responses/code-mode-helper-compat.ts — compile view_image / default.view_image to await tools.view_image(...) + image(result.image_url), with file_path / file / image_path aliases mapped onto Codex's path. Do not compile it as exec_command, and do not replace it with a "not available in Code Mode" text() error. Code mode does have this nested helper.
  3. Tests in tests/responses/legacy-shell-compat.test.ts, tests/adapters/bridge-legacy-shell-normalization.test.ts, tests/responses/responses-undeclared-tool-guard.test.ts.

Please do not land #4171 as-is for this. That draft accepts the name (good) but compiles a soft-fail telling the model to use exec_command to inspect pixels (wrong). exec_command cannot inject an image the way tools.view_image + image() does, so the model will retry or give up. Maintainer review on #4171 already flagged the title/behaviour mismatch.

Additional context

Related:

I searched existing provider/compatibility issues. Request/response above are redacted. Expected behaviour is the Codex code-mode exec nested-helper contract plus the existing helper remap pattern in this repo.

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 reportsstreamingSSE, WebSocket, terminal stream framestoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions