Skip to content

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

Closed

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

  1. added
    catalogModel catalog, slugs, visibility, routed entries
    providerProvider adapters, OpenAI-compat presets, upstream API quirks
    streamingSSE, WebSocket, terminal stream frames
    toolstool_calls, MCP, web-search / sidecar tools
    on Sep 12, 2026
  2. lidge-jun commented on Sep 12, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 74 / 80

    이 이슈는 Codex Desktop 코드 모드에서 라우팅된 모델이 스크린샷을 찍은 뒤 이미지를 보려 할 때, 스트림이 중간에 끊기는 버그다. 코드 모드 요청 카탈로그에는 보통 최상위 도구로 exec만 선언되고, 이미지는 await tools.view_image(...) 같은 중첩 헬퍼로만 열려 있다. 그런데 OpenCode 계열 모델(예: muse-spark)은 와이어에 default.view_image를 그대로 보낸다. 현재 dev(aa0dd5086)의 미선언 도구 가드(src/server/responses-undeclared-tool-guard.ts, 브리지 쪽 메시지)는 그걸 “요청에 없는 클라이언트 도구”로 보고 undeclared_tool_call / upstream_error로 턴을 죽인다. 사용자는 보면 그냥 “stream disconnected…”로 끝난다.

    이미 dev에는 #4176 / #4264로 “카탈로그에 bare view_image가 있을 때 발명된 default. 접두사를 벗기는” 축이 있다. 그건 src/types/tools.ts의 normalizeDeclaredToolName이 bare가 선언된 경우에만 동작한다. 코드 모드에서는 bare view_image가 애초에 없어서 그 수정이 여기엔 안 먹는다. 같은 파일의 CODE_MODE_HELPER_TOOL_NAMES는 지금 exec_command, shell_command, write_stdin, apply_patch만 있고 view_image는 없다. src/responses/code-mode-helper-compat.ts의 compileCodeModeHelperInput도 apply_patch / write_stdin / exec_command만 컴파일한다. 그래서 이슈가 제안한 표(카탈로그가 exec만일 때 view_image / default.view_image → exec + tools.view_image + image())는 현재 헬퍼 리맵 패턴과 정확히 같은 축이다.

    예상 동작도 코드베이스와 맞다. Codex 코드 모드 exec 안에 tools.view_image({ path, detail? })와 image(image_url)가 실제로 있고, 성공 시 custom_tool_call_output에 input_image가 실린다. 이걸 exec_command로 픽셀을 읽으라고 소프트페일하면(#4171 초안이 그 방향) 이미지가 클라이언트에 안 들어가서 모델이 재시도하거나 포기한다. 이슈 본문이 #4171을 as-is로 랜딩하지 말라고 한 이유는 타당하다. 형제 실패(예: undeclared TaskUpdate)는 같은 “OpenCode 네이티브 이름이 클라이언트가 광고하지 않은 채로 새는” 부류지만, 이 티켓은 view_image 헬퍼 리맵만 다룬다.

    재현도 명확하다. 코드 모드 + opencode-go muse-spark로 창 캡처 후 PNG 검사 → 성공한 exec들 뒤에 default.view_image → 가드가 502/스트림 끊김. 버전은 2.51.0과 현재 dev 둘 다라고 적혀 있고, #4264 이후에도 남는다. 라벨(provider-compatibility / tools / streaming)과도 맞다.

    라인 - 해당 없음(이슈 본문; 수정은 아직 PR 없음)

    src/types/tools.ts · CODE_MODE_HELPER_TOOL_NAMES - view_image가 헬퍼 목록에 없어 default.view_image가 exec로 정규화되지 않는다
    src/responses/code-mode-helper-compat.ts · compileCodeModeHelperInput - view_image를 tools.view_image + image()로 컴파일하는 분기가 없다
    src/server/responses-undeclared-tool-guard.ts - 정규화 실패 시 fail-closed라 턴이 통째로 죽는다(의도된 가드지만 헬퍼 누락의 증상)
    PR #4171 - 이름 수락은 맞지만 text() 가용성 에러로 컴파일하면 이미지 주입이 안 되어 증상만 완화된다

    메인테이너의 판단이 필요한 지점

    • #4171을 닫고 새 PR로 올바른 컴파일 타깃을 가져갈지, #4171을 강제 리라이트할지
    • path 별칭(file_path / file / image_path → path) 범위를 어디까지 허용할지
    • TaskUpdate 등 다른 OpenCode 네이티브 누출을 같은 마일스톤에 묶을지, view_image만 먼저 할지

    너의 추천

    • 이슈는 유효한 P0급 코드모드 호환 버그로 두고, #4171은 “잘못된 컴파일 타깃”으로 닫거나 크게 고치라고 코멘트한다.
    • 새 수정 PR은 (1) CODE_MODE_HELPER에 view_image 추가, (2) default. 접두사를 헬퍼 매칭 전에 벗기기(exec 선언·헬퍼 미선언일 때), (3) compileCodeModeHelperInput에 tools.view_image + image() 경로, (4) legacy-shell / undeclared-guard 회귀 테스트 — 이슈 Suggested mapping 그대로.
    • bare view_image가 카탈로그에 있으면 fix(responses): normalize invented default. namespace back to the declared bare tool #4264 동작을 유지하고, exec도 view_image도 없으면 계속 fail-closed.

    이 댓글은 grok-bot이 작성했습니다

  3. lidge-jun commented on Sep 13, 2026

    @lidge-jun
    Owner

    Fixed on dev. The code-mode exec catalog no longer rejects routed default.view_image as undeclared: #4475 landed inside lane R's tip #4489 (merge commit 3f76ce4, verified as an ancestor of origin/dev).

    Two contributors are credited in the landed commit, @jeongjin0 for #4455 and @rrmlima for #4171, since both pull requests answered this issue and the review findings on the smaller one were folded into the carry.

    Cross-platform CI run 34746891233 concluded success on the exact merged head. Closing manually because this merges into dev rather than the default branch.

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