Claude API 400 invalid_request_error: 원인과 해결법 (2026)
Claude API 400 invalid_request_error는 JSON이 malformed이거나 required field가 누락된 경우에 발생합니다. 잘못된 요청 형식이며, 재시도하지 말고 요청 자체를 수정해야 합니다. 이 글은 5가지 흔한 원인과 Python/TypeScript 코드 예시를 다룹니다.
전반적인 Claude API 에러 처리 패턴은 Claude API Error Handling 가이드를 참고하세요.
무엇을 의미하는가?
400 HTTP 상태 코드는 JSON이 malformed이거나 required field가 누락된 경우을 의미합니다. Anthropic API의 에러 응답 본문에는 error.type이 "invalid_request_error"로 명시되며, error.message에 구체적 사유가 옵니다.
응답 예시:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "..."
}
}
흔한 원인 5가지
- messages 배열 형식 오류 (role/content 필수)
- max_tokens가 모델 한도 초과 (예: Sonnet 4.5는 8192)
- temperature가 0~1 범위 밖
- tool_choice 형식 오류
- model 이름 오타 (예: claude-sonnet-4 → claude-sonnet-4-5)
해결 코드 (Python)
# Validate before sending
def safe_request(client, model, messages, max_tokens=1024):
# Sonnet 4.5 max_tokens cap
if model.startswith("claude-sonnet") and max_tokens > 8192:
max_tokens = 8192
if not messages or not isinstance(messages, list):
raise ValueError("messages must be a non-empty list")
for msg in messages:
if msg["role"] not in ("user", "assistant"):
raise ValueError(f"invalid role: {msg['role']}")
return client.messages.create(
model=model, messages=messages, max_tokens=max_tokens,
)
해결 코드 (TypeScript)
function safeRequest(client, model, messages, maxTokens = 1024) {
if (!Array.isArray(messages) || messages.length === 0) {
throw new Error("messages must be a non-empty array");
}
if (model.startsWith("claude-sonnet") && maxTokens > 8192) {
maxTokens = 8192;
}
for (const msg of messages) {
if (!["user", "assistant"].includes(msg.role)) {
throw new Error(`invalid role: ${msg.role}`);
}
}
return client.messages.create({ model, messages, max_tokens: maxTokens });
}
상세 원인별 수정 방법
1. messages 배열 형식 오류
messages 필드는 {"role": "user"|"assistant", "content": "..."} 객체의 배열이어야 합니다. 가장 흔한 실수는 단일 문자열을 전달하거나, role을 "system"으로 쓰는 경우입니다. Claude API는 system을 별도 top-level 파라미터로 받습니다.
# 잘못된 예
messages = "안녕하세요" # 문자열 → TypeError 또는 400
# 올바른 예
messages = [{"role": "user", "content": "안녕하세요"}]
2. max_tokens 한도 초과
모델별 최대 응답 토큰 수:
| 모델 | max_tokens 최대값 |
|---|---|
| claude-haiku-4-5 | 8,192 |
| claude-sonnet-4-5 | 8,192 |
| claude-opus-4 | 32,768 |
max_tokens=100000 처럼 지나치게 큰 값을 설정하면 즉시 400이 반환됩니다.
3. temperature 범위 이탈
temperature는 0.0 이상 1.0 이하여야 합니다. OpenAI API의 경우 2.0까지 허용하지만 Claude API는 다릅니다. 다른 provider에서 마이그레이션할 때 자주 발생하는 문제입니다.
재시도하지 마세요
이 에러는 클라이언트 측 문제라 재시도해도 같은 결과입니다. 위 원인을 확인하고 요청을 수정한 뒤 재발송하세요.
비용 영향
이 에러는 요청이 처리되지 않았으므로 비용이 청구되지 않습니다. 단, 잘못된 모델로 요청을 반복하다가 다른 에러가 발생할 수 있으니 모니터링이 필요합니다.
자세한 비용 절감 패턴은 Claude API Cost and Prompt Caching Break-Even 또는 무료 비용 계산기를 참고하세요.
빠른 진단 체크리스트
요청을 보내기 전 아래 항목을 점검하세요:
-
messages배열이 비어 있지 않고, 각 메시지에role과content필드가 모두 있는가? -
role값이"user"또는"assistant"둘 중 하나인가? -
max_tokens가 사용 모델의 최대치를 초과하지 않는가? (Sonnet 4.5: 8192, Opus 4: 32768) -
temperature가 0 이상 1 이하 범위 안에 있는가? -
model문자열에 오타가 없고, 현재 지원되는 이름인가? (예:claude-sonnet-4-5)
프로덕션 모니터링
400 에러는 재시도할 필요가 없으므로, 발생 즉시 로깅하여 요청 스키마 버그를 조기에 발견하는 것이 중요합니다.
import logging, anthropic
logger = logging.getLogger("claude.errors")
def monitored_request(client, **kwargs):
try:
return client.messages.create(**kwargs)
except anthropic.BadRequestError as e:
# 400 invalid_request_error — 재시도 없이 즉시 알림
logger.error(
"400 invalid_request_error",
extra={
"model": kwargs.get("model"),
"error_message": str(e),
"messages_count": len(kwargs.get("messages", [])),
}
)
# Slack/PagerDuty 알림: 코드 버그 가능성 높음
raise
로그 집계 시 error_message 패턴을 분석하면 어떤 필드가 반복적으로 문제가 되는지 파악할 수 있습니다. 같은 에러 메시지가 10회 이상 반복된다면 배포 코드에 버그가 있다는 신호입니다.
언제 지원팀에 연락해야 하나요?
400 에러는 대부분 클라이언트 수정으로 해결됩니다. 아래 경우에만 support.anthropic.com에 문의하세요:
- 요청 형식이 공식 문서와 완전히 일치하는데도 계속 400이 반환되는 경우
- 동일한 요청이 간헐적으로는 성공하고 간헐적으로는 400이 뜨는 경우 (API 측 버그 가능성)
-
error.message에 "internal" 또는 "unexpected" 같은 서버 측 단어가 포함된 경우 - SDK 최신 버전으로 업그레이드했음에도 동일 에러가 지속되는 경우
관련 에러
- 400 invalid_request_error — 잘못된 요청 형식
- 401 authentication_error — 인증 실패
- 403 permission_error — 권한 부족
- 429 rate_limit_error — Rate limit 초과
- 529 overloaded_error — API 일시 과부하
자주 묻는 질문
400 에러가 떴을 때 비용이 청구되나요?
처리되지 않은 요청이므로 청구되지 않습니다. 단, 무한 재시도 루프는 다른 에러로 비용을 발생시킬 수 있습니다.
400와 다른 에러의 차이는?
400는 클라이언트 측 문제로 요청 수정 없이는 재시도해도 같은 결과입니다. 5xx 에러 (500/529)는 일시적 서버 이슈라 재시도가 효과적입니다.
Bedrock/Vertex에서도 같은 에러 코드인가요?
네, Anthropic의 error.type 명명 규칙은 모든 deployment (Direct API, AWS Bedrock, GCP Vertex AI)에서 동일합니다. 다만 HTTP 상태 코드는 platform 별로 wrapping되어 다를 수 있습니다 (예: Bedrock은 400을 ValidationException으로 wrap).
다음 단계
- 프로덕션 코드에 retry 로직을 추가하려면 Production Patterns for Claude Agents를
- 비용을 모니터링하려면 Claude API 비용 모니터링 가이드를
- 에러 분류 자동화는 무료 /cheatsheet-한국어에서 30개 프롬프트로
에러 처리 패턴 30개 + Pydantic 검증 코드 — Claude API Cost Optimization 마스터클래스 ($59)에 retry 미들웨어, 비용 가드레일, 에러 알림 패턴 12편 포함.