Repository navigation
[bug] code-mode exec catalog rejects routed default.view_image as undeclared #4412
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.* 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:
- When the request catalog is genuine code mode (bare
execdeclared, no top-levelview_image), a provider-emittedview_imageor inventeddefault.view_imageis not an undeclared client tool. - The call is restored as the declared
execcustom 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.
- When the catalog does declare top-level
view_image, keep [bug] routed provider prefixes bare Codex tool withdefault.:default.view_imagerejected as undeclared #4176 / fix(responses): normalize invented default. namespace back to the declared bare tool #4264 behaviour: inventeddefault.view_imagerewrites to bareview_imageand is not compiled intoexec. - When neither
execnorview_imageis declared, keep fail-closed.
Minimal redacted request or reproduction
- Codex Desktop, code mode, model
opencode-go/muse-spark-1.3-contributor(or any OpenCode-native model that emitsdefault.*tools). - Ask the model to screenshot a window and inspect the PNG.
- Observed sequence: many successful
exec/tools.exec_commandcalls, then a top-level tool call nameddefault.view_image. - 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
- Codex code-mode nested helper (observed on the wire):
tools.view_image({ path, detail? })thenimage(result.image_url). - OpenCodex fail-closed undeclared-tool guard: [Bug]: OpenCode Go native Responses path leaks undeclared apply_patch and shows aborted #1700 /
src/server/responses-undeclared-tool-guard.ts. - Invented
default.prefix when bareview_imageis declared: [bug] routed provider prefixes bare Codex tool withdefault.:default.view_imagerejected as undeclared #4176, fixed ondevby fix(responses): normalize invented default. namespace back to the declared bare tool #4264. That fix does not apply here, because code mode never declares bareview_image.
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):
src/types/tools.ts— addview_imagetoCODE_MODE_HELPER_TOOL_NAMES. Whenexecis declared anddefault.<helper>was not itself declared, strip the inventeddefault.prefix before helper→execnormalization. Otherwisedefault.view_imagenever matches the helper list.src/responses/code-mode-helper-compat.ts— compileview_image/default.view_imagetoawait tools.view_image(...)+image(result.image_url), withfile_path/file/image_pathaliases mapped onto Codex'spath. Do not compile it asexec_command, and do not replace it with a "not available in Code Mode"text()error. Code mode does have this nested helper.- 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:
- [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 — inventeddefault.prefix when the bare tool is in the catalog. - fix(responses): gracefully handle code-mode view_image calls via unified exec #4171 — draft "gracefully handle code-mode view_image" via a
text()availability error. Same symptom, wrong compile target. - [bug] muse-spark via opencode-go: undeclared client tool "default.apply_patch" kills subagent turns #3402 / fix(proxy): accept dotted ns.name tool echo alongside ns__name #3403 — dotted
ns.namealiases for tools that were declared with a namespace. Code-modeview_imagewas never declared underdefault. - Sibling failure mode already in the wild: routed
opencode-go/deepseek-flashemitting undeclaredTaskUpdateon Claude Desktop. Same class (OpenCode-native tool name leaking onto a client that never advertised it). This issue is only aboutview_image.
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
lidge-jun commentedon Sep 12, 2026
리뷰 · 우선순위 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로 “카탈로그에 bareview_image가 있을 때 발명된default.접두사를 벗기는” 축이 있다. 그건src/types/tools.ts의normalizeDeclaredToolName이 bare가 선언된 경우에만 동작한다. 코드 모드에서는 bareview_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로 랜딩하지 말라고 한 이유는 타당하다. 형제 실패(예: undeclaredTaskUpdate)는 같은 “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() 가용성 에러로 컴파일하면 이미지 주입이 안 되어 증상만 완화된다
메인테이너의 판단이 필요한 지점
너의 추천
이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Sep 13, 2026
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.
fix(responses): execute code-mode view_image through unified exec [sk…
Merge upstream 2.54.0 into the combined cache trial
fix(responses): execute code-mode view_image through unified exec [sk…
fix(responses): emit the tool name the undeclared-tool guard authoriz…
fix(responses): emit the tool name the undeclared-tool guard authoriz…