Repository navigation
Anthropic 400: tools.N.custom.input_schema.type Field required (tool schema missing top-level type) #75
Description
요약 / Summary
Anthropic(Claude) 백엔드로 사용할 때, Codex가 서브에이전트/브라우저/코덱스 툴 등 추가 툴을 붙이는 순간 아래 400이 발생합니다.
Provider error 400: {"type":"error","error":{"type":"invalid_request_error","message":"tools.26.custom.input_schema.type: Field required"},"request_id":"req_011CcqiYNzcinns1gsUkV9Cg"}
When using an Anthropic (Claude) backend, requests fail as soon as extra tools are attached (sub-agent, browser use, Codex tools). Anthropic rejects a tool whose input_schema lacks a top-level type.
환경 / Environment
- Package:
@bitkyc08/opencodex@2.6.31-preview.20260707(same code path exists upstream) - OS: Windows
- Backend: Anthropic (Claude Opus)
- Trigger: only when the tool list grows (sub-agent / browser / Codex tools). Basic chat without those tools works fine.
원인 / Root cause
src/adapters/anthropic.ts 의 toolsToAnthropicFormat 가 input_schema 로 t.parameters 를 그대로 전달합니다. 일부 Codex/MCP/서브에이전트/브라우저 툴의 파라미터 스키마에는 최상위 type: "object" 가 없습니다. OpenAI 와이어는 이를 허용하지만 Anthropic Messages API 는 input_schema.type 을 필수로 검증하므로 400 을 반환합니다.
toolsToAnthropicFormat passes t.parameters straight through as input_schema. Some Codex/MCP/sub-agent/browser tools ship a parameter schema without a top-level type: "object". The OpenAI wire tolerates this, but the Anthropic Messages API requires input_schema.type, so it 400s. The failing index (e.g. tools.26) is one of the extra tools, which is why plain chat works but adding tools breaks.
const converted = tools.map(t => ({
name: toolNames.toWire(namespacedToolName(t.namespace, t.name)),
description: t.description,
input_schema: t.parameters, // <- schema without top-level `type` is forwarded verbatim
}));제안 수정 / Proposed fix
input_schema 를 Anthropic 요구사항에 맞게 정규화합니다 (최상위 type 누락 시 object 보정 + properties 보장).
// Anthropic requires every tool's `input_schema` to be a JSON Schema object with an
// explicit top-level `type` (it validates `tools.N.custom.input_schema.type`). Codex/
// OpenAI Responses tool params frequently omit it, which the OpenAI wire tolerates but
// Anthropic 400s on. Normalize so a missing/invalid top-level `type` becomes an object
// schema with `properties`.
function normalizeAnthropicInputSchema(schema: unknown): Record<string, unknown> {
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
return { type: "object", properties: {} };
}
const s: Record<string, unknown> = { ...(schema as Record<string, unknown>) };
if (typeof s.type !== "string") s.type = "object";
if (s.type === "object" && (s.properties === undefined || s.properties === null)) {
s.properties = {};
}
return s;
}
// ...in toolsToAnthropicFormat:
input_schema: normalizeAnthropicInputSchema(t.parameters),재현 / Reproduction
- Configure an Anthropic (Claude) backend.
- In Codex, trigger a sub-agent, browser use, or Codex tools so the tool list grows.
- Request 400s with
tools.N.custom.input_schema.type: Field required.
로컬에서 위 패치 적용 후 서브에이전트/브라우저/코덱스 툴 호출이 정상 동작하는 것을 확인했습니다. Verified locally: after applying the patch, sub-agent/browser/Codex tool calls work. Happy to open a PR if the approach looks good.
Activity
seotk0319 commentedon Jul 9, 2026
정정 / Correction — the
type-only fix is not enough원래 제안한 "최상위
type보정"만으로는 해결되지 않았습니다. Anthropic Messages API는input_schema루트에oneOf/anyOf/allOf콤비네이터가 있으면 그것도 거부합니다. Codex/OpenAI Responses 쪽 일부 툴(그리고 MCP 툴)이 루트에 union 스키마를 그대로 내보내는 경우가 있어서,type만 채워도 같은 400이 계속 납니다.Adding a top-level
typealone does not fix it. The Anthropic Messages API also rejects a top-leveloneOf/anyOf/allOfoninput_schema. Some Codex/OpenAI Responses (and MCP) tools emit a root-level union schema, so filling intypestill 400s.완전한 원인 / Full root cause
input_schema루트에type누락 → 거부input_schema루트에oneOf/anyOf/allOf존재 → 거부 (프로퍼티 내부에 중첩된anyOf등은 허용)완전한 수정 / Complete fix (
src/adapters/anthropic.ts)루트 콤비네이터를 평탄화합니다: 각 branch의
properties를 루트 object 스키마로 병합,required는allOf에서만 병합(oneOf/anyOf는 과도 강제 방지), 중첩된anyOf는 그대로 보존, 그 외 루트 키(title,additionalProperties등)는 유지.toolsToAnthropicFormat내부는 동일하게input_schema: normalizeAnthropicInputSchema(t.parameters)로 연결합니다.검증 / Verification
파일에 적용한 실제 함수 본문을 그대로 실행하여 다음 케이스를 확인했습니다:
type누락 →{type:"object",properties:{}}anyOf/oneOf→ 각 branch properties 병합, required 강제 안 함allOf→ properties + required 병합 (["p","r"])anyOf(프로퍼티 내부) → 원형 그대로 보존title,additionalProperties) → 여분 키 유지, 콤비네이터만 제거이 상태에서 서브에이전트/브라우저/Codex 툴 호출이 모두 정상 동작합니다. Original title/body should be read together with this correction. Happy to open a PR with this complete version.
Ingwannu commentedon Jul 9, 2026
Confirmed valid and opened a fix PR: #76
What I verified:
devwas forwarding Anthropicinput_schema: t.parametersverbatim.properties/requiredbut no roottypestayed missingtype.anyOfschema also stayed at the root, matching the corrected report here.Fix in #76:
input_schemato a roottype: "object"schema.propertiesexists.oneOf/anyOf/allOfwhile preserving nested property schemas.requiredonly forallOf, notoneOf/anyOf.Validation:
bun test tests/anthropic-tool-schema.test.ts tests/anthropic-empty-content.test.ts tests/kiro-adapter.test.ts-> 38 passbun run typecheck-> passbun test ./tests/-> 1677 pass, 0 failThe PR body includes
Fixes #75, so this issue should close automatically when the PR is merged.lidge-jun commentedon Jul 9, 2026
Fixed and verified. The Anthropic adapter now normalizes tool input_schema roots so schemas without a top-level type are sent as object schemas, and the regression coverage is in tests/anthropic-tool-schema.test.ts.\n\nVerified locally with:\n- bun test tests/anthropic-tool-schema.test.ts tests/anthropic-empty-content.test.ts tests/kiro-adapter.test.ts\n- bun test tests/multi-agent-compat.test.ts tests/native-model-toggle.test.ts\n- npm run build (gui)\n- bun run typecheck\n\nAlso verified on dev Cross-platform CI: https://github.com/lidge-jun/opencodex/actions/runs/29034128441