Claude API thinking 400 오류: disabled·between_tools·effort 해결
Sonnet 5에서 사고를 끄던 {"type": "disabled"}를 모델 ID만 바꿔 Sonnet 5.5에 보내면 400이 납니다. 이걸 컴파일 단계에서 미리 막을 수 있을까 싶어 공식 TypeScript SDK로 그 요청을 타입 검사해 봤는데, 아무 오류 없이 통과하더라고요.
잘못된 요청 모양 셋 가운데 컴파일에서 잡힌 건 between_tools에 display를 붙인 경우 하나뿐이었습니다. 나머지는 서버에 보내 봐야 드러난다는 뜻이죠.
Claude API의 thinking 400은 대부분 요청의 thinking.type 값이 그 모델이 받는 값과 어긋난 것입니다. Claude Sonnet 5.5에서 사고를 끄려면 "disabled" 대신 "between_tools"를 effort high 이하로 보내고, Opus 5.5·Fable 5.1처럼 사고를 끌 수 없는 모델에서는 thinking 필드를 빼세요.
2026년 10월 4일 Anthropic 공식 문서(사고 문제 해결, 오류 레퍼런스, Sonnet 5.5 마이그레이션 가이드, 릴리스 노트) 기준입니다. 오류 문구는 공식 문서와 공개 이슈에서 옮겼습니다. 직접 실행한 것은 공식 SDK로 요청 모양을 타입 검사한 것으로, Ubuntu 24.04에서 한 번, 10월 13일 Windows 11에서 TypeScript SDK 0.133.0·Python SDK 1.13.0으로 한 번 더 했습니다.
확인한 버전·환경
- Anthropic TypeScript SDK (@anthropic-ai/sdk) 0.133.0 (TypeScript 5.9.3, Node.js 24) · Windows 11 · Windows PowerShell 5.1 · 확인일 2026-10-13
- Anthropic Python SDK (anthropic) 1.13.0 · Windows 11 · Windows PowerShell 5.1 · 확인일 2026-10-13
- Anthropic TypeScript SDK (@anthropic-ai/sdk) 0.131.0 · 0.128.0 (TypeScript 5.9.3, Node.js 22) · Ubuntu 24.04 · 확인일 2026-10-04
- Anthropic Python SDK (anthropic) 1.11.0 · Ubuntu 24.04 · 확인일 2026-10-04
오류 문구 앞부분으로 가르기
400 메시지는 앞부분만 봐도 어느 경우인지 갈립니다. 아래 문구는 공식 문서에 실린 형태입니다.
| 오류 문구의 시작 | 원인 | 고칠 것 |
|---|---|---|
"thinking.type.disabled" is not supported … Use "thinking.type.adaptive" |
끌 수 없는 모델에 disabled |
thinking 필드 빼기 |
To turn thinking off on this model, send … "between_tools" |
Sonnet 5.5에 disabled |
between_tools로 |
"thinking.type.between_tools" is not supported |
Sonnet 5.5가 아닌 모델 | 그 모델이 받는 값으로 |
output_config.effort 'xhigh' is not supported when thinking is disabled |
between_tools + xhigh·max |
effort를 high 이하로 |
messages.N: output_config.effort 'low' differs from … |
between_tools에서 대화 중 effort 변경 |
메시지별 effort 제거 |
"thinking.type.enabled" is not supported |
4.7 이후 모델에 budget_tokens 방식 |
adaptive + effort로 |
adaptive thinking is not supported on this model |
4.5 이하 확장 사고 전용 모델 | enabled + budget_tokens |
“끌 수 없는 모델”은 Fable 5.1, Mythos 5.1, Fable 5, Mythos 5, Opus 5.5입니다. Mythos Preview에 disabled를 보냈을 때의 문구는 따로 있고, Opus 5가 effort xhigh·max에서 disabled를 거절할 때의 문구는 확인한 세 문서(사고 문제 해결, 오류 레퍼런스, 마이그레이션 가이드)에 없습니다.
오류 문자열로 분기하는 코드는 조심하세요. 공개 이슈에 실린 9월 29일의 Sonnet 5.5 응답은 위 표의 문구와 다릅니다(아래 공개 사례 절).
Sonnet 5.5에서 사고 끄기는 between_tools로
Sonnet 5에서 사고를 끄던 코드를 모델 ID만 바꿔 옮기면 이 400이 납니다.
To turn thinking off on this model, send "thinking": {"type": "between_tools"} instead of {"type": "disabled"}. The model does not think before responding. The short updates it writes between tool calls come back as thinking blocks.
이유는 간단합니다. Sonnet 5.5가 받는 thinking.type은 "adaptive"와 "between_tools" 둘뿐이고, Sonnet 5는 "adaptive"와 "disabled"를 받습니다. Sonnet 5.5는 thinking 필드가 없는 요청도 adaptive 사고로 돌리고요.
"thinking": { "type": "between_tools" },
"output_config": { "effort": "high" }
effort를 낮춰도 소용없습니다. Sonnet 5.5에서 disabled는 어느 effort에서나 400이에요.
between_tools는 사고를 완전히 없애는 스위치가 아니라 가장 낮은 사고 설정입니다. 답하기 전에는 생각하지 않지만, 도구 호출 사이에 쓰는 짧은 진행 메모는 요약 텍스트가 든 thinking 블록으로 돌아옵니다. 도구가 없으면 응답에 텍스트만 있고요.
그래서 응답을 content[0].text처럼 위치로 읽던 코드는 type으로 블록을 골라 읽도록 함께 고쳐야 합니다.
같은 이유로 between_tools를 쓴다고 사고 비용이 0이 되지는 않습니다. 도구를 쓰는 요청이면 그 진행 메모가 thinking 블록으로 돌아오니까요. 확인한 문서들은 between_tools의 청구량을 따로 적지 않으니, 실제 사용량은 응답의 토큰 수로 확인하세요.
between_tools는 Sonnet 5.5를 제공하는 모든 플랫폼에서 베타 헤더 없이 동작합니다.- 서버 측 폴백으로 요청이 Sonnet 5로 넘어가면 그쪽에서는
{"type": "disabled"}로 실행됩니다.
between_tools로 바꿨는데 또 400이라면
between_tools는 effort high 이하에서, 다른 필드 없이, Sonnet 5.5에만 보내야 합니다. 이 조건 중 하나라도 어긋나면 다시 400을 만납니다.
output_config.effort ‘xhigh’ is not supported when thinking is disabled on this model
between_tools는 effort low·medium·high에서만 받고 xhigh·max면 400입니다. 사고를 끈 적이 없는데 “thinking is disabled”라고 나오는 건, between_tools에 답하기 전 사고가 없어서 그렇게 표시되기 때문이에요. 문구의 'xhigh' 자리에는 보낸 값이 들어갑니다.
effort를 high 이하로 내리세요. xhigh나 max가 꼭 필요하면 thinking 필드를 빼거나 {"type": "adaptive"}를 보냅니다. {"type": "enabled"}는 Sonnet 5.5가 그것도 400으로 거절합니다.
effort cannot change when thinking is disabled on this model
첫 요청은 통과했는데 대화 중간에 messages.N: output_config.effort 'low' differs from the 'high' in effect before it;로 시작하는 400이 나는 경우입니다. between_tools에서는 대화 도중 effort를 바꿀 수 없고, messages.N은 새 값을 지정한 메시지의 위치예요.
그 메시지별 effort를 지우거나 적용 중인 값과 같게 맞추세요. 턴마다 effort를 바꿔야 하는 앱이라면 adaptive 사고를 씁니다.
effort나 사고 방식을 바꾸는 순간 생기는 일이 하나 더 있습니다. 사고 설정과 effort 값은 캐시되는 앞부분의 일부라서, 사고 방식이나 effort, budget_tokens를 바꾸면 메시지 캐시 중단점이 무효가 되고, 모델에 따라 도구·시스템 프롬프트 쪽 중단점도 무효가 될 수 있습니다.
thinking 설정을 바꾼 뒤 프롬프트 캐시 적중이 사라졌다면 이것부터 의심해 보세요. 캐시 조건은 프롬프트 캐싱이 실제로 싸지는 조건에 정리했습니다.
between_tools와 함께 다른 필드를 보낸 경우
{"type": "between_tools", "display": "omitted"}처럼 display·budget_tokens·block_binding을 함께 보내면 400입니다. 이 경우의 오류 문구는 확인한 세 문서에 없습니다.
{"type": "between_tools"}만 남기세요. adaptive용 display나 block_binding을 모든 요청에 공통으로 붙이는 코드라면 between_tools 경로에서 빼야 합니다. Sonnet 5.5에서 block_binding은 adaptive에서만 동작하고, between_tools에서는 대화 기록을 덧붙이기만 하거나 수정한 턴부터 사고 블록을 걷어 냅니다.
“thinking.type.between_tools” is not supported for this model.
between_tools를 받는 모델은 Sonnet 5.5 하나뿐입니다. 모델을 바꿨다면 그 모델이 받는 값으로 되돌리세요. 여러 모델을 오가는 앱이라면 “사고 끄기”를 모델별 값으로 바꾸는 자리를 한 곳에 두는 게 좋습니다.
사고를 끌 수 없는 모델: Opus 5.5·Fable 5.1
Fable 5.1, Mythos 5.1, Fable 5, Mythos 5, Opus 5.5는 사고가 항상 켜져 있고(문서 표기 “Always on”) "enabled"와 "disabled"를 둘 다 400으로 거절합니다. thinking 파라미터를 빼면 설정 없이도 사고해요. 사고를 끄려던 목적이 응답에서 사고 텍스트를 빼는 것이었다면 display: "omitted"를 쓰고, 사고량을 줄이려면 effort를 낮춥니다.
같은 disabled가 어디서 통하고 어디서 막히는지는 모델별 표로 보는 게 정확합니다.
| 모델 | 기본값 | 400으로 거절하는 값 |
|---|---|---|
| Fable 5.1 · Mythos 5.1 · Fable 5 · Mythos 5 · Opus 5.5 | Always on | enabled, disabled |
| Opus 5 | On | enabled, disabled(effort xhigh·max) |
| Sonnet 5.5 | On | enabled, disabled |
| Sonnet 5 | On | enabled |
| Opus 4.8 · Opus 4.7 | Off | enabled |
| Opus 4.6 · Sonnet 4.6 | Off | 없음(enabled는 deprecated) |
| Opus 4.5 · Haiku 4.5 · Sonnet 4.5 | Off | adaptive |
Mythos Preview는 adaptive와 확장 사고를 모두 지원하고 "disabled"만 거절합니다. 표에 없는 값은 받아들입니다.
주의할 점이 하나 있습니다. Opus 5에서 effort high 이하로 사고를 끄면, 응답이 가끔 도구 호출을 tool_use 블록 대신 텍스트로 쓰거나 <thinking> 같은 태그를 본문에 내보냅니다. 검색처럼 도구를 많이 쓰는 작업에서 가장 흔해요.
400은 아니지만, 그 설정을 다른 모델로 옮기기 전에 이 증상이 있었는지도 보세요.
budget_tokens를 쓰던 코드라면
사고를 켜는 쪽에서도 400이 납니다. 확장 사고({"type": "enabled", "budget_tokens": N})는 Claude 4.6 모델에서 deprecated지만 아직 성공하고, Claude 4.7 이후 모델은 지원하지 않습니다.
"thinking.type.enabled" is not supported for this model. Use "thinking.type.adaptive" and "output_config.effort" to control thinking behavior.
{"type": "adaptive"}로 바꾸고 사고 깊이는 effort로 조절하세요. 예산과 effort 사이에 정해진 대응은 없으니, effort 두세 단계로 평가를 돌려 맞는 값을 고르는 것이 공식 권장입니다.
반대 방향도 있습니다. Claude 4.5 이하의 확장 사고 전용 모델에 adaptive를 보내면 adaptive thinking is not supported on this model이 나오고, 이 모델들에는 enabled와 budget_tokens를 그대로 씁니다. 같은 코드가 4.5와 5.5 계열을 함께 부른다면 thinking 값을 모델별로 갈라야 해요.
SDK 타입 검사로 어디까지 잡히나
400을 받기 전에 컴파일 단계에서 걸러지는지 Ubuntu 24.04 · Node.js 22 테스트 환경에서 직접 실행해 봤습니다. 공식 TypeScript SDK @anthropic-ai/sdk 0.131.0과 TypeScript 5.9.3으로, Sonnet 5.5에 보내는 요청 네 개를 타입만 검사했습니다(요청은 서버로 가지 않습니다).
| 요청 모양 | API(문서 기준) | 타입 검사 |
|---|---|---|
between_tools + effort high |
정상 | 통과 |
disabled |
400 | 통과(못 잡음) |
between_tools + effort xhigh |
400 | 통과(못 잡음) |
between_tools + display |
400 | 오류로 잡음 |
npx tsc --noEmit --strict --module nodenext --moduleResolution nodenext --target es2022 --skipLibCheck check.ts
check.ts(35,38): error TS2769: No overload matches this call.
…
Object literal may only specify known properties, and 'display' does not exist in type 'ThinkingConfigBetweenTools'.
exit=2
10월 13일 Windows 11 · Node.js 24에서 그사이 나온 0.133.0으로 같은 네 요청을 다시 검사해 봤습니다. 결과는 그대로였어요. display를 붙인 요청만 같은 오류로 잡혔고, disabled와 effort xhigh는 이번에도 통과했습니다.
SDK 타입에서 ThinkingConfigBetweenTools는 type: 'between_tools' 하나만 가진 인터페이스라 다른 필드를 쓰면 잡힙니다. 반면 ThinkingConfigParam은 enabled | disabled | between_tools | adaptive 네 타입의 합집합이고 모델 ID와 묶여 있지 않아서, 어느 모델이 어느 값을 거절하는지는 타입이 알 수 없어요. 타입 검사를 통과했다고 안심하면 안 되는 이유입니다.
between_tools가 들어가기 직전 버전인 0.128.0에서는 반대로 올바른 첫 번째 요청이 막혔습니다.
check.ts(9,15): error TS2769: No overload matches this call.
…
Type '"between_tools"' is not assignable to type '"enabled" | "disabled" | "adaptive"'.
exit=2
SDK 변경 기록의 0.129.0(2026-09-28) 항목에 between_tools 사고 타입과 claude-sonnet-5-5가 함께 추가됐습니다. disabled를 between_tools로 고쳤는데 컴파일이 안 된다면 400 문제가 아니라 SDK가 0.129.0보다 오래된 것이니 SDK부터 올리세요.
Python SDK는 1.11.0에 ThinkingConfigBetweenToolsParam 타입이 들어 있고, 1.13.0에서도 type이 between_tools 하나뿐인 같은 모양이었습니다.
다른 사람들이 겪은 문제
Sonnet 5.5 출시(9월 28일) 직후 서로 다른 저장소에 같은 문제가 올라왔습니다.
앞의 두 건에서 disabled를 보낸 것은 사용자가 아니라, 사용자의 “사고 끄기” 선택을 요청으로 바꾸는 앱 쪽 코드였습니다. 세 번째는 같은 문제를 400 대신 사고 비용을 떠안는 쪽으로 처리한 경우입니다.
- simplexsigil(2026-09-29, ThunderAI#936): Thunderbird 확장에서
claude-opus-5-5나claude-sonnet-5-5를 고르면 어떤 프롬프트든 HTTP 400으로 실패하고, 원인은 확장이"thinking": {"type": "disabled"}를 보내기 때문이라고 보고했습니다. Claude Sonnet 4.x 같은 이전 모델은 같은 설정으로 계속 동작했습니다. - abacha(2026-09-29, openclaw#161113): 사고를 끈 실행(
--thinking off)만 400이 났고,--thinking medium과 사고 옵션을 뺀 실행은 둘 다 HTTP 200이었다고 보고했습니다. 보고자 분석으로는 이 400이 사고 블록 재전송 오류로 잘못 분류돼 같은 요청을 한 번 더 보낸 뒤 다른 모델로 넘어갔고, 운영자가 본 증상은 모델 폴백 표시뿐이라 알아채는 데 약 20시간이 걸렸습니다. 사고를 끌 수 없는 모델 목록에는 Fable 5와 Mythos 5만 있었다고 합니다. - ThomasK33(2026-09-28, coder/xum#5086): 이 프로젝트는 400을 피하려고 Sonnet 5.5의 “off”를
low수준 adaptive 사고로 바꾸고 있어서, “off”를 고른 사용자도 사고 비용을 낸다고 적었습니다.between_tools로 바꾸는 조건으로 effortxhigh·max금지, 대화 중 effort 변경 금지,display·budget_tokens·block_binding동반 금지, 기록을 고칠 때의 사고 블록 처리를 들었고, 쓰고 있는@ai-sdk/anthropic4.0.58이between_tools를 받지 않을 수 있다고 덧붙였습니다.
두 번째 사례에서 보듯, 폴백 장치가 있는 앱에서는 400이 오류 화면이 아니라 “왜 답이 약해졌지”로 나타날 수 있습니다.
두 이슈에 실린 API 메시지는 공식 문서의 Sonnet 5.5 문구와 다릅니다. ThunderAI 쪽은 Opus 5.5와 Sonnet 5.5 중 어느 모델의 응답인지 적혀 있지 않지만, 어느 쪽이든 문서의 문구와 다릅니다.
"thinking.type.disabled" is not supported for this model. Use "thinking.type.between_tools" for the lowest thinking setting, or "thinking.type.adaptive" and "output_config.effort" to control thinking behavior.
출시 직후 문구가 바뀐 것인지, 조건에 따라 다른 것인지는 확정되지 않았습니다. 그래서 코드에서 오류 문구를 그대로 비교하는 건 권하지 않습니다.
서명 오류 끝의 설명 문장은 문구가 바뀔 수 있으니 코드에서 비교하지 말라는 안내도 문서에 있어요. 분기는 오류 문자열이 아니라 “어느 모델에 어떤 thinking·effort를 보냈는가”라는 요청 쪽 정보로 하는 편이 안전하고, 다른 400에도 같은 대비를 해 두는 편이 낫습니다.
마치며
이 400들을 정리하고 나서 남은 생각은, “사고 끄기”가 더 이상 값 하나가 아니라는 것입니다. Sonnet 5.5는 between_tools, Opus 5.5·Fable 5.1은 필드 빼기, Sonnet 5·Opus 4.8은 disabled이고, SDK 타입은 그중 다른 필드를 붙인 경우만 잡아 줍니다. 모델을 바꿀 때마다 이걸 기억에 맡기기보다는 코드에 박아 두는 편이 낫다고 봅니다.
그래서 지금 코드에서 "disabled"를 보내는 자리를 찾아, 모델별 값으로 바꾸는 함수 하나로 모아 두길 권합니다. Opus 5.5로 옮기는 중이라면 사고 외의 호환성 깨짐도 Claude Opus 5.5 vs Fable 5.1 비교 글에서 함께 확인해 보세요.
참고한 공식 자료
모두 2026년 10월 4일에 확인했습니다.
- Troubleshooting thinking — 모델별 지원 값·기본값·거절 표와 400 문구별 원인·해결
- Errors — “Thinking cannot be disabled” 절의 모델별 오류 문구,
between_tools와 effort 오류 - Migrating to Claude Sonnet 5.5 —
between_tools조건, 모델별thinking.type표, 사고 예산 제거 - Claude Platform release notes — 2026년 9월 28일 Claude Sonnet 5.5 출시, Sonnet 5용 코드가 깨지는 다섯 가지
- anthropic-sdk-typescript CHANGELOG — 0.129.0(2026-09-28)의
between_tools사고 타입 추가
인용한 공개 사례
- simplexsigil, 2026-09-29, micz/ThunderAI#936 — Claude API: 400 error with Claude Opus 5.5 / Sonnet 5.5 — thinking.type.disabled not supported
- abacha, 2026-09-29, openclaw/openclaw#161113 — Anthropic transport sends thinking.type.disabled to Claude Sonnet 5.5, which rejects it
- ThomasK33, 2026-09-28, coder/xum#5086 — Map the “off” thinking level to between_tools on Claude Sonnet 5.5
변경 이력
- 2026-10-13 — Windows 11에서 TypeScript SDK 0.133.0으로 같은 네 요청을 다시 타입 검사해, display를 붙인 요청만 잡히고 disabled와 effort xhigh는 여전히 통과한다는 결과와 Python SDK 1.13.0의 between_tools 타입을 더했습니다.