Claude의 Invalid signature in thinking 블록 수정 방법 — 히스토리는 추가만 가능
요약
Claude Fable 5.1 등 최신 모델에서 멀티턴 에이전트가 'Invalid signature in thinking block' 오류를 겪는 현상과 해결책을 제시합니다. 이 문제는 API 버그가 아닌, 'preserved thinking' 사양 때문에 발생하며, 이전 대화의 사고 블록(thinking block)을 잘라내어 전송할 때 문제가 생깁니다.
핵심 포인트
- 최신 Claude 모델에서 멀티턴 에이전트 사용 시 400 에러 발생 가능성이 높습니다.
- 오류는 API 버그가 아닌, 'preserved thinking' 사양에 따른 정상 동작입니다.
- 해결을 위해 `thinking-binding-controls` 헤더와 `prefix_mismatch_behavior: "drop_block"` 설정을 사용해야 합니다.
- 이 문제는 이전 assistant 턴을 되돌려 보내는 에이전트 루프에서 주로 발생합니다.
Claude Fable 5.1로 마이그레이션하면서 멀티턴 에이전트가 이 400 에러에서 멈춘다는 문의가 늘고 있습니다. 2026년 9월에 출시된 Opus 5.5와 Sonnet 5.5, 그리고 10월 7일의 Haiku 5.5에도 같은 메커니즘이 적용되었기 때문에, 앞으로는 대부분의 최신 모델에서 발생할 것입니다.
messages.5.content.0: Invalid `signature` in `thinking` block. The block is bound to a different conversation. Remove the block, or set `thinking.block_binding.prefix_mismatch_behavior` to "drop_block". That setting requires the `thinking-binding-controls-2026-08-01` value in the `anthropic-beta` header.
이것은 API의 버그가 아니라, preserved thinking 이라는 사양(Fable 5.1 / Mythos 5.1 / Opus 5.5 / Sonnet 5.5 / Haiku 5.5)입니다. 무엇이 일어나고 어떻게 수정하는지, 2026년 10월 기준 공식 문서를 바탕으로 정리합니다.
직접 확인한 내용
10월 8일에 실제 API로 '추가만 하는 것은 문제없지만, 맨 앞을 잘라내면 실패한다'는 것을 확인했습니다. thinking-binding-controls-2026-08-01 헤더와 prefix_mismatch_behavior: "drop_block" 설정을 추가하고, effort low로 '1에 1을 더하면?' 같은 짧은 질문을 3턴 쌓고, 마지막에 맨 앞의 1턴 분량을 잘라내서 전송했습니다.
claude-opus-5-5
추가만 1턴째 content=['text'] input_transformations=[]
추가만 2턴째 content=['text'] input_transformations=[]
...
Opus 5.5는 예상대로였습니다. 3턴째 응답에 붙어 있던 사고 블록(thinking block)이 맨 앞을 잘라냄으로써 '다른 대화의 것'으로 판정되어 messages.3.content.0 로 떨어졌습니다. 만약 drop_block이 아니었다면, 여기는 시작 부분의 400 에러가 발생했을 것입니다.
Fable 5.1은 맨 앞을 잘라내도 아무 일도 일어나지 않았습니다. 이유는 간단해서, effort low의 쉬운 질문이었기 때문에 3턴 모두 사고하지 않고 답변했고, 따라서 사고 블록이 하나도 없었기 때문입니다. 검사 대상은 사고 블록이므로, 없는 것은 떨어지지 않습니다. Opus 5.5 역시 1, 2턴째는 사고를 하지 않았습니다.
이는 실무에서 까다로운 특성입니다. 같은 히스토리 편집 코드라도, 모델이 해당 턴에 생각했는지 여부에 따라 400 에러가 나거나 안 날 수 있습니다. 간단한 입력 테스트에서는 통과하고, 실제 복잡한 입력에서 처음으로 멈추는 경우가 발생합니다. 아래의 '검사하는 3단계'에서 테스트를 실무와 유사한 입력으로 돌리도록 작성된 이유입니다.
먼저, 자신의 코드가 영향을 받는지 확인해 보았습니다. Claude API를 호출하는 프로젝트 3개(자막 정형 파이프라인, 독서 지도 웹 앱, 유가증권 보고서를 읽는 일일 작업)는 모두 1 요청으로 완료되는 호출이었습니다. messages는 매번 user 턴 1개만 사용했고, 이전 응답을 되돌려 보내지 않았습니다. 이 검사는 '이전 응답에 포함되어 있던 사고 블록을 되돌려 보냈을 때'에만 작동하기 때문에, 이런 단발성 호출은 마이그레이션해도 아무 일도 일어나지 않습니다. 첨부된 scan.py에서도
하지만 히스토리 자르기(H15)는 3개 모두 0건이었습니다.
문제가 되는 것은 도구를 사용하는 에이전트 루프나 채팅처럼, 이전 assistant 턴을 되돌려 보내는 코드뿐입니다. 자신의 코드가 어느 쪽에 속하는지는 messages에 role: "assistant"를 쌓고 있는지로 판단할 수 있습니다.
유가증권 보고서 일일 작업은 또 다른 참고 자료가 되었습니다. 하나의 PDF에 3개의 분석을 실행하기 위해, system
(공통 루브릭)→ PDF → 분석별 지시 순서로 배열하고, 가변적인 부분은 반드시 끝에 배치합니다. 캐시를 위한 설계이지만, '이전 내용은 변경하지 않고 뒤에 추가하는' 형태는 이 기사의 근본적인 해결책과 완전히 동일합니다.
무슨 일이 일어나고 있는가
Fable 5.1 (및 Opus 5.5 / Sonnet 5.5 / Haiku 5.5)의 thinking 블록의 signature는 그것을 생성한 대화의 접두사(최상위 system, tools 집합, 해당 블록 이전의 모든 메시지)를 기록합니다. 히스토리를 다시 보낼 때 API는 이 접두사가 변경되지 않았는지 검사합니다. 만약 변경되었다면, 그 블록 이후의 사고(thinking) 블록은 무효화됩니다.
이는 2026년 8월 31일 이후에 생성된 새 계정에서 강제되며, 향후 모델에서는 모든 계정에 적용될 예정입니다. 오래된 계정이라도 헤더나 prefix_mismatch_behavior를 전송하는 순간부터 적용됩니다.
Sonnet 5.5와 Haiku 5.5에는 또 다른 계정 경계가 있습니다. 이 사고 블록은 그것을 만든 조직(또는 연결된 조직)에서만 읽을 수 있으며, 다른 조직에서 보내면 조용히 버려집니다. 대화 로그를 조직 간에 이동시키는 메커니즘이 있다면 확인해 주십시오.
무효화되는 작업 (빈도순)
- 오래된 턴 자르기:
messages = messages[-20:],messages.pop(0) - 시스템 프롬프트 재구성: 요청마다
system을 다시 조립하는 것 (날짜나 상태 임베딩) - 리마인더 삽입 및 삭제: '남은 토큰'이나 '주의사항'을 직전 턴에 넣고 다음 요청에서 지우는 것
- 도구 배열 변경: 도구를 중간에 추가하거나 제거하는 것
- 오래된 도구 결과 삭제: 문맥을 절약하기 위해
tool_result를 지우는 것 - 이미지 URL 내용 변경: 이전 턴의 URL이 나중에 다른 바이트열을 반환하는 경우
반대로, 다음은 유효한 상태입니다: 추가만 하는 히스토리, `role:
| 하던 것 | 대신 |
|---|---|
세션 도중에 system을 수정 | 시작 시 고정하고, 변경된 시점에 {"role": "system", "content": "..."}를 messages에 추가 (GA, 헤더 불필요) |
중간에 tools를 수정 | 시작 시 전부 선언 (숨길 것은 defer_loading: true)하고, tool_addition / tool_removal 블록을 role: "system"으로 전송 (베타 mid-conversation-tool-changes-2026-07-01. inline-tools-2026-09-15라면 정의별로 작성 가능) |
| 리마인더를 넣고 다음 차례에 지우기 | clear_at: "next_user_message"의 턴 스코프 시스템 메시지를 tool_result 뒤에 배치하여 기록하고 남기기 (베타 mid-conversation-system-clear-at-2026-08-21). 베타가 아니라면 같은 user 메시지의 tool_result 뒤에 텍스트 블록으로 배치하고, 오래된 복사본도 남기기 |
| 오래된 도구 결과 삭제, 턴 축소 | 서버 측 context editing (context-management-2025-06-27) 또는 compaction (compact-2026-01-12). 검사는 "당신이 보낸 대화"에 대해 수행되며, 서버가 편집한 결과는 대상에서 제외. 직접 구분하고 싶다면 온디맨드 compaction (compact-2026-09-04)으로 서명된 요약 블록을 받아 다음 요청의 맨 앞에 배치하기 |
| 클라이언트 측 compaction | 간단한 compaction: 대화를 하나의 요약으로 묶고, 다음 요청은 그 요약 + 새로운 user 턴만으로 시작. 이전 턴도 생각 블록도 반환하지 않음 |
| 이미지/문서를 URL로 전 턴 참조 | Files API에 한 번 업로드하여 file_id 또는 base64 사용 |
클라이언트 측 compaction에서 깨지는 형태가 2가지 있습니다. keep-tail(오래된 턴을 요약하고, 직전 턴은 원문으로 남기기)는, 남긴 턴의 생각 블록이 전체 히스토리가 전제되어 만들어졌기 때문에 400 에러가 발생합니다. 비동기 compaction(뒤에서 요약하여 대체하는 것)도 마찬가지입니다. 둘 다 남긴 턴의 생각 블록을 제거하거나, drop_block을 계속 보내야 합니다. 히스토리의 중간 턴을 건너뛰는 작업은 어떤 형태로든 후속 작업을 전부 무효화합니다.
자신의 루프를 검사하는 3단계
- 일반적인 몇 턴 분량의 요청 본문을 기록하고, 인접한 요청에서
system・tools・messages의 공통 접두사가 바이트 단위로 일치하는지 비교하기 -thinking-binding-controls-2026-08-01
와drop_block을 사용하여 일반적인 멀티턴을 흘려보내고, 매 응답의input_transformations를 기록하기. 모든 턴이 빈 배열이면 문제없습니다. 다만, 모델이 생각하지 않은 턴은 검사되지 않으므로, 실제와 같은 난이도의 입력으로 흘려보내야 합니다 (간단한 입력이라고 생각하면 생각 블록이 나오지 않아, 깨져 있어도 빈 배열이 됩니다) - 실제 설정을 명시적으로 정하고 모니터링하기. CI에서는 `
Fable 5.1의 세 가지 파괴적 변경 사항(강제 도구 호출 (400), 사고 블록의 모델 제약, 그리고 이 히스토리 검사)과 메시지 단위의 노력(effort) 또는 턴 범위 시스템 메시지와 같은 새로운 기능 사용법은 Zenn의 책 『Claude 5 세대 전환 가이드』의 제6장 및 제10장에 걸쳐 작성되어 있습니다. 제1장부터 제2장은 무료입니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기