Skip to content

[Bug]: Flattened tool names exceed 64-char gateway limits (Command Code 400: "name must be at most 64 characters, got 66") #4679

Closed

Description

@HulianBuligon

Client or integration

opencodex 2.55.0 (@bitkyc08/opencodex, Docker node:22-slim) running as the Responses→Chat translation proxy in front of the Command Code provider (api.commandcode.ai, openai-chat adapter). Client: Codex Desktop / codex-cli 0.153.x.

Summary

When a Codex client declares many MCP/app tools, namespace-tool-compat flattens namespaced tools to <namespace>__<name> for chat gateways. Some strict gateways cap function names at 64 characters. Command Code's AI gateway rejects the whole request before inference:

{"error":{"message":"`name` must be at most 64 characters, got 66","type":"invalid_request_error"}}

The 66-char name in that gateway error is mcp__codex_apps__codex_document_control___execute_document_command. Other real offenders (Codex Desktop built-in app tools — the user cannot exclude them from the bundle):

  • mcp__codex_apps__codex_document_control___get_document_tool_schemas (67)
  • mcp__codex_apps__safety_settings___prepare_parental_control_update (65)

This bites exactly on Responses-Lite catalogs (use_responses_lite: true), where the client bundles every declared tool into the additional_tools input item — there is no way to shrink the surface from config.

Reproduction

  1. Run opencodex 2.55.0 behind any client with the standard Codex Desktop tool set, routed to Command Code.
  2. Send any turn with use_responses_lite: true.
  3. → 400 name must be at most 64 characters, got 66 from the gateway; the turn fails.

Suggested fix

A bounded, deterministic, reversible alias at the single point where wire names are produced — namespacedToolName() / dottedToolName() in src/types/tools.ts — so declarations, history replay, toolNsMap/declaredToolNames (built in server/responses/collaboration.ts) and the undeclared-tool guard all stay consistent, and provider echoes restore to the native {namespace, name} automatically:

// alias = flat.slice(0, 64 - 13) + "_" + sha256(`${ns ?? ""}\0${name}\0${attempt}`).slice(0, 12)
  • stable across restarts (digest of the native identity, not declaration order);
  • collision-safe loop (claimed-alias set, attempt bump);
  • no dotted spelling for a bounded alias (it is already at the limit);
  • restore falls out for free because the bridge keys toolNsMap by the same function's output.

Precedent: codex-router special-cases exactly this as BOUNDED_TOOL_NAME_PROVIDERS = {"commandcode", "commandcode-messages"} with BOUNDED_TOOL_NAME_LENGTH = 64 (src/chat-tool-surface.mjs + deterministic truncate+digest aliases in src/namespace-relay.mjs).

Version

opencodex 2.55.0 (@bitkyc08/opencodex npm).

Operating system

Linux (Docker, node:22-slim) for the proxy; macOS 26 arm64 for the Codex client.


A PR implementing the suggested fix is up and validated in production: #4715. A long-name tool round-trips (function_call returns with namespace: "mcp__codex_apps__safety_settings" restored), and a meta/muse-spark-1.3 root session spawned a deepseek-v4.1-flash subagent end-to-end through Command Code.

Activity

  1. github-actions commented on Sep 15, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  2. changed the title [-]Flattened tool names can exceed 64-char gateway limits (Command Code 400: "name must be at most 64 characters, got 66")[/-] [+][Bug]: Flattened tool names exceed 64-char gateway limits (Command Code 400: "name must be at most 64 characters, got 66")[/+] on Sep 15, 2026
  3. added
    bugSomething isn't working
    toolstool_calls, MCP, web-search / sidecar tools
    on Sep 15, 2026
  4. lidge-jun commented on Sep 15, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 70 / 80

    이 이슈는 Codex Desktop / Responses-Lite 가 MCP·앱 도구를 많이 실어 보낼 때, OpenCodex 가 이름공간 도구를 <namespace>__<name> 로 납작하게 만든 뒤 Command Code(api.commandcode.ai, openai-chat 어댑터) 게이트웨이로 넘기면 함수 이름 길이 상한 64자를 넘겨 HTTP 400 이 난다는 실사용 버그다. 본문에 나온 예시는 전부 Codex 내장 앱 도구이고 사용자가 카탈로그에서 빼기 어렵다. 예: mcp__codex_apps__codex_document_control___get_document_tool_schemas(67자). 재현 조건은 use_responses_lite: true 와 표준 Desktop 도구 묶음이면 충분하고, 보고 버전이 2.55.0 이어도 지금 dev(HEAD 3ea88f3db, 패키지 2.56.0) 에서도 같은 납작화 경로가 그대로다.

    현재 dev 를 보면 비슷한 문제는 이미 한 경로만 막혀 있다. #4422 가 머지한 src/responses/muse-tool-name-alias.ts 는 64자·문자셋 제한에 맞춰 충돌 없는 해시 별칭을 만들고 왕복 복원까지 한다. 그런데 게이트는 isMetaAiResponsesDestination 이라 호스트가 api.meta.ai 일 때만 켜진다. Command Code 프리셋(commandcode → https://api.commandcode.ai/provider/v1, adapter openai-chat) 은 그 게이트 밖이라 #4422 혜택을 받지 못한다. 와이어 이름은 여전히 src/types/tools.ts 의 namespacedToolName / dottedToolName 과 src/responses/namespace-tool-compat.ts · openai-chat tool-schema.ts 가 그대로 길게 만든다.

    작성자 제안(네이티브 정체성 해시 + 선언·히스토리·toolNsMap 일관 별칭)은 방향이 맞다. 다만 전역으로 namespacedToolName 자체를 무조건 자르면 Meta Muse 전용 복원 맵·Kiro 정규화·점(.) 별칭 계약과 어긋날 수 있다. 더 안전한 축은 (1) muse 별칭 모듈을 “64자 제한 게이트웨이” 공통 레이어로 일반화하고 Command Code / commandcode 호스트를 포함시키거나, (2) openai-chat outbound 한곳에 provider 플래그로 같은 플랜을 거는 것이다. 본문에 “PR이 올라와 프로덕션 검증됐다”고 적혀 있으나 이 저장소 open PR 목록과 #4679 타임라인에는 연결된 PR 번호가 없다. 포크 PR이면 여기로 링크해 달라는 상태다. types.ts/config.ts 스플릿·godfile facade 와는 무관해서 close-don't-rebase 대상이 아니다.

    라인 / 경로 수준:

    라인 30–32 (src/types/tools.ts namespacedToolName) - 길이 상한 없이 namespace__name 만 만든다. Chat 게이트웨이 64자 거절의 직접 입력이다
    라인 41–43 (dottedToolName) - 점 별칭도 같은 길이 문제를 공유한다. 전역 truncate 를 넣으면 별칭 이중 등록을 조심해야 한다
    라인 16–21 · 245+ (src/responses/muse-tool-name-alias.ts) - #4422 해결책이 이미 있다. 게이트가 api.meta.ai 전용이라 Command Code 에 안 닿는다
    라인 330–331 (src/adapters/openai-responses/passthrough.ts) - Responses passthrough 에서만 muse rewrite 호출. openai-chat Command Code 경로는 여기 안 탄다
    경로 src/providers/registry/entries-extended.ts id: "commandcode" - adapter openai-chat, baseUrl api.commandcode.ai/provider/v1. 이슈 재현 프리셋과 일치한다
    경로 src/adapters/openai-chat/tool-schema.ts - 선언 시 namespacedToolName 을 그대로 name 필드에 넣는다. 별칭 플랜이 없다면 66자 이름이 그대로 나간다

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

    • muse 별칭을 호스트 집합(commandcode 포함)으로 넓힐지, openai-chat 전용 bounded-name 레이어를 새로 둘지
    • 별칭을 namespacedToolName 전역에 넣을지(작성자 제안) vs outbound 어댑터에서만 넣을지(기존 fix(responses): alias tool names over 64 chars for Meta Muse wire compatibility #4422 패턴)
    • 작성자가 말한 “이미 올린 PR”이 이 레포에 안 보이니, 링크/번호를 받은 뒤 그걸 기준으로 리뷰할지 새 PR을 받을지
    • Responses-Lite additional_tools 전체 번들 축소(설정으로 줄이기)는 우회일 뿐 근본 수정이 아니다 — 제품 방향으로 둘지

    너의 추천
    이슈는 유효하고 우선순위 높게 둔다. 먼저 작성자에게 이 레포 PR 링크를 요청하고, 없으면 #4422 의 muse-tool-name-alias 패턴을 Command Code(api.commandcode.ai / commandcode openai-chat)까지 확장하는 단일 수정 PR을 받는 쪽을 권한다. namespacedToolName 전역 truncate 는 다른 프로바이더 계약을 깨기 쉬우니 기본안으로 두지 말 것. 중복 이슈·types/config 무효화로 닫을 대상이 아니다. PR이 오면 선언·히스토리 리플레이·undeclared-tool 가드·복원 맵이 한 플랜을 쓰는지와 66→≤64 왕복 회귀 테스트만 확인하면 된다.

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

  5. HulianBuligon commented on Sep 15, 2026

    @HulianBuligon
    ContributorAuthor

    The PR is up: #4715 (fork PR from HulianBuligon:fix/bounded-tool-wire-names → dev, 2 commits).

    On the design question (global bound in namespacedToolName vs the #4422 muse-alias pattern gated per provider): I went with the single-point bound deliberately, for two reasons:

    1. The 64-char cap is the de-facto wire contract of chat-completions function names, not a Command Code quirk — OpenAI's own function name limit is 64. A flattened name over 64 is already invalid on the most common strict consumer, so bounding it at the name-producing function matches the format's contract rather than any one provider's policy.
    2. Blast radius is measured: names ≤64 are byte-identical; only >64 changes, and those names fail today on strict gateways anyway. The alternative (provider-gated at the openai-chat outbound, fix(responses): alias tool names over 64 chars for Meta Muse wire compatibility #4422-style) needs the provider identity threaded through buildToolBridgeMaps, the undeclared-tool guard and history replay — five surfaces — while buildToolBridgeMaps keys the restore map by this same function's output, so restoration stays free of extra plumbing.

    On the interaction with #4422's muse-tool-name-alias: in the Meta destination path my bound runs first, so that module now sees ≤64 names — strictly less work, no contract change. If you still prefer provider-gating, I'm happy to rework #4715 along those lines.

    Since the review: pushed 8f6fa49 collapsing the duplicate toolChoiceAliases entry for bounded aliases ([alias, alias] → [alias]) with a regression test — suite is now 5 pass / 0 fail.

  6. HulianBuligon commented on Sep 15, 2026

    @HulianBuligon
    ContributorAuthor

    Production datapoint from the same setup (opencodex behind a routing proxy → Command Code, meta/muse-spark-1.3 as the root model on the v2 surface) that strengthens the case for widening the #4422 bare/dotted echo acceptance to the commandcode destination:

    Observed live today — during a root turn that spawned two subagents (deepseek-v4.1-flash + muse-spark-1.3), Command Code's muse echoed the spawn tool call with the bare name:

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

    The declared wire names on that request were the flattened forms (collaboration__spawn_agent, plus the unambiguous dotted alias collaboration.spawn_agent). The guard's fail-closed rejection killed the stream mid-turn, the Codex client showed "Reconnecting 1/5" and re-sent the whole turn; the retry succeeded (both subagents completed). So it self-heals, but every occurrence costs a full round-trip plus a re-send of the entire context.

    This matches the exact quirk muse-tool-name-alias.ts (#4422) exists for — the muse family dropping/altering the namespace spelling on echo — currently gated to api.meta.ai destinations only, while commandcode (api.commandcode.ai, openai-chat) goes through the strict guard.

    Two candidate shapes, in increasing scope:

    1. Guard-side leniency: accept a bare echo when exactly one declared identity claims that bare name (the ambiguity machinery to resolve this already exists in collectAmbiguousDottedAliases/buildToolBridgeMaps — bare names are only claimed for functions-namespace children today).
    2. Host-set widening: extend muse-tool-name-alias's destination gate to include api.commandcode.ai / the commandcode provider id.

    Happy to fold either into #4715 as a follow-up commit or split it into a second PR — your call on the preferred shape.

  7. added a commit that references this issue on Sep 16, 2026
  8. lidge-jun commented on Sep 16, 2026

    @lidge-jun
    Owner

    Landed via #4774 at 6151ff4

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

    bugSomething isn't workinglanded-via-maintainerOriginal PR closed after landing via a maintainer merge traintoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions