Repository navigation
[bug] routed provider prefixes bare Codex tool with default.: default.view_image rejected as undeclared #4176
Description
Client or integration
Codex App
Area
Proxy / routing / tool-name normalization
Summary
A routed non-OpenAI model can emit the Codex view_image tool as default.view_image, which OpenCodex rejects as undeclared and aborts the stream:
stream disconnected before completion: routed provider emitted undeclared client tool "default.view_image"; only request-declared tools may be called
This still reproduces on OpenCodex 2.48.0.
The observed failure is similar to #3402 / #3403, but appears to be a different alias case.
#3403 fixed a declared namespaced tool whose provider echo used a different spelling:
default__apply_patch <-> default.apply_patch
This report is about a bare Codex tool being returned with an invented/default namespace:
view_image -> default.view_image
If the request declares bare view_image but does not declare default.view_image / default__view_image, the existing dotted ns.name alias support does not necessarily identify these as the same tool.
Current behaviour
- Codex exposes/uses the local
view_imagetool. - A routed custom/non-OpenAI model or upstream compatibility layer returns the function call name as
default.view_image. responses-undeclared-tool-guardtreats it as a different, undeclared client tool.- The SSE stream is aborted.
- Codex App shows reconnect attempts (
reconnecting /5) and the turn fails.
Expected behaviour
OpenCodex should safely normalize a provider-added default. namespace back to the unique bare request-declared tool when that mapping is unambiguous.
For example, if the request-declared tool catalog contains exactly one bare tool named:
view_image
and does not contain a real namespaced tool whose canonical/alias name is default.view_image, then a provider echo of:
default.view_image
could be restored to:
view_image
before the undeclared-tool guard rejects it.
This should remain fail-closed when the mapping is ambiguous or the bare tool is not actually declared.
Suggested safety constraints
A conservative normalization could apply only when all of the following are true:
- The returned name has the form
default.<name>. <name>is present as a request-declared bare tool.default.<name>/ the corresponding namespaced identity is not itself a declared tool.- The mapping resolves to exactly one declared tool identity.
Otherwise preserve the current undeclared-tool rejection.
This would avoid broadly stripping arbitrary namespaces while handling providers that serialize un-namespaced function calls under a synthetic default namespace.
Reproduction
- Run OpenCodex 2.48.0 with Codex App routed through a custom/non-OpenAI provider.
- Use a task that causes the model to inspect an image through Codex
view_image. - The routed provider returns
default.view_image. - Observe:
stream disconnected before completion: routed provider emitted undeclared client tool "default.view_image"; only request-declared tools may be called
Codex App then attempts to reconnect repeatedly.
Version
OpenCodex 2.48.0
Operating system
Windows
Provider and model
Custom / non-OpenAI routed model. The issue appears to be at the tool-call naming/compatibility boundary rather than in Codex's native OpenAI model path.
Related issues
- [bug] muse-spark via opencode-go: undeclared client tool "default.apply_patch" kills subagent turns #3402 —
default.apply_patchrejected when the declared namespaced spelling wasdefault__apply_patch - fix(proxy): accept dotted ns.name tool echo alongside ns__name #3403 — accepts dotted
ns.namealongsidens__name - Unexpected reconnection using /goal #2260 / Feature: per-provider opt-out of code_mode_only tool mode for routed models (deepseek-v4-flash: undeclared exec_command aborts stream, reconnect then fails reasoning replay with DeepSeek 400) #2106 / normalizeDeclaredToolName maps exec_command→exec only when "exec" is declared; bare "exec" calls abort streams in shell mode #2641 — other routed-provider tool-name/catalog mismatches that abort the stream
Additional context
Prompt-level guidance in AGENTS.md was added to tell non-OpenAI models to use only request-declared tools and exact tool names, including explicitly avoiding invented prefixes such as default.. The failure still occurs, suggesting the default. prefix may be introduced by the model/provider tool-call serialization path rather than being reliably preventable through prompt instructions alone.
Checks
- Existing issues were searched before filing.
- No secrets, API keys, account identifiers, or credentials are included.
- This report distinguishes the bare-tool
view_image -> default.view_imagecase from the namespaced spelling fix in fix(proxy): accept dotted ns.name tool echo alongside ns__name #3403.
Activity
lidge-jun commentedon Sep 10, 2026
리뷰 · 우선순위 69 / 80
이 이슈는 Codex App이 라우팅된 비-OpenAI 모델로
view_image를 쓸 때, 업스트림이 도구 이름을default.view_image로 돌려보내면 OpenCodex가 선언되지 않은 클라이언트 도구로 보고 SSE를 끊는 버그입니다. 재현 메시지는 정확히src/server/responses-undeclared-tool-guard.ts/src/bridge.ts가 쓰는 문장입니다.routed provider emitted undeclared client tool "…"; only request-declared tools may be called. 2.48.0에서도 난다고 했고, 지금dev(c15a98caa, package 2.50.0)의 가드 로직을 보면 같은 거절이 그대로입니다.이미 비슷한 sor를 고친 적이 있습니다. #3403(머지됨)은 이미 네임스페이스로 선언된 도구가
ns__name과ns.name두 철자로 메아리칠 때 둘 다 같은 신원으로 받도록src/responses/tool-name-aliases.ts와 undeclared-tool-guard를 고쳤습니다. #3402는default.apply_patch↔default__apply_patch쪽 관측이었습니다. 이번 보고는 그다음 구멍입니다. 요청 카탈로그에는 bareview_image만 있는데, 공급자/직렬화 계층이 합성 네임스페이스default.를 붙여 돌려보냅니다. #3403 별칭은 “선언된 namespace+name”에서 출발하므로, bare만 있는 경우에는default.view_image를view_image로 되돌리지 않습니다.src/types/tools.tsnormalizeDeclaredToolName도 code-modeexec헬퍼 이름만 다루고,default.접두를 벗기지 않습니다. 그래서 가드가declared.has("default.view_image")에서 실패하고 스트림이 죽습니다. Codex App은 reconnect /5를 돌다 턴이 실패합니다. 프롬프트(AGENTS.md)로 “default. 붙이지 말라”고 적어도, 모델/프로바이더 직렬화가 붙이는 경우에는 막지 못한다는 작성자 관찰도 타당합니다.형제 PR #4171은 code-mode
view_image를 unified exec로 넘기는 다른 축입니다. 이 이슈의 “bare ↔ default.bare 정규화”와 겹치지 않습니다. 중복 이슈로 닫지 말고, 가드/별칭 쪽 작은 패치로 받는 게 맞습니다. types/config 분할과도 무관합니다.라인 문제:
src/server/responses-undeclared-tool-guard.ts undeclaredNameInItem - bare 호출 경로에서
normalizeDeclaredToolName후declared.has만 봅니다.default.view_image→view_image폴백이 없어 스트림이 즉시 거절됩니다.src/types/tools.ts normalizeDeclaredToolName - code-mode helper만 정규화합니다. 합성
default.네임스페이스는 범위 밖이라 이 버그를 막지 못합니다.src/responses/tool-name-aliases.ts / collectDeclaredWireToolNames - #3403은 선언된 namespace에서 dotted alias를 등록합니다. bare 선언만 있을 때 공급자가 붙인
default.를 읽어 되돌리는 경로는 아직 없습니다.src/bridge.ts (undeclared client tool 분기) - 가드가 이름을 거절하면 그대로 연결을 끊습니다. 재연결 루프의 직접 원인입니다.
관련 #4171 - view_image 단어는 같지만 code-mode/exec 호환 이슈라서 이 티켓을 Fixes로 묶으면 안 됩니다.
메인테이너의 판단이 필요한 지점
default.<bare>이고, bare가 선언되어 있고,default.<bare>/default__<bare>가 따로 선언되지 않았고, 매핑이 유일할 때만 허용)을 그대로 받을지normalizeDeclaredToolName/별칭 레이어로 올릴지default외 다른 합성 네임스페이스까지 열지, 아니면default만 narrowly 허용할지너의 추천
이슈를 열린 채로 두고, 위 네 조건을 지키는 작은 fail-closed 패치 PR을 받으세요. #3403을 넓히는 방향이 자연스럽고, #4171과는 분리하세요. 스트림을 끊는 실사용 버거라 우선순위는 중상입니다. 패치 PR이 오면 privacy/typecheck +
default.view_image→view_image단위 테스트 후 merge하면 됩니다.이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Sep 11, 2026
Fixed on
devby #4264.An invented
default.namespace is normalized back to the declared bare tool when — and only when — the caller actually declared that bare name.Credit to @chilung-cgu, whose commits were carried unchanged into the release chain.
Closing manually: pull requests here target
dev, so GitHub'sCloseslinkage only fires on a merge intomain. This will reach users in the next release.fix(responses): emit the tool name the undeclared-tool guard authoriz…
fix(responses): emit the tool name the undeclared-tool guard authoriz…