가이드
오류 처리
CheaperGen API의 전체 오류 코드 및 HTTP 상태 코드 (400, 401, 402, 404, 409, 429, 502, 503), 발생 원인과 처리 또는 재시도 방법
API는 일반적인 HTTP 상태 코드를 사용합니다. 2xx은 성공, 4xx은 요청을 수정해야 함, 5xx는 당사 또는 상위 서비스에서 오류가 발생했음을 뜻합니다. 모든 오류 응답은 같은 JSON 구조이므로 한곳에서 처리할 수 있습니다.
오류 형식#
{
"error": {
"code": "invalid_request",
"message": "\"resolution\" must be one of: 480p, 720p",
"param": "resolution"
}
}code— 안정적이고 기계가 읽을 수 있는 문자열입니다. 메시지가 아니라 이 값을 기준으로 분기하세요.message— 로그에 안전하게 남길 수 있는 사용자용 설명입니다. 문구는 바뀔 수 있습니다.param— 특정 요청 필드가 오류 원인일 때 포함됩니다.
오류 코드#
| HTTP | 코드 | 의미 | 해결 방법 | 재시도? |
|---|---|---|---|---|
| 400 | invalid_request | 요청 본문이 유효한 JSON이 아니거나 필수 필드가 빠졌거나, 값이 범위를 벗어나거나 허용되지 않거나, 알 수 없는 매개변수가 포함되어 있거나, 프롬프트에서 참조한 항목을 찾을 수 없는 경우, 또는 참조 동영상·오디오 파일이 읽을 수 없거나 모델 한도 (길이, 크기, 해상도, 프레임 레이트)를 벗어난 경우입니다. param에 문제가 발생한 필드가 표시됩니다. | 요청을 수정하세요. 오류 메시지에 정확한 문제가 표시됩니다. | 수정한 뒤 다시 제출하세요. |
| 401 | unauthorized | API 키가 없거나 형식이 잘못되었거나 폐기되었습니다. | 헤더 Authorization: Bearer를 확인하고 키가 활성 상태인지 확인하세요. | 수정한 뒤 다시 제출하세요. |
| 402 | insufficient_balance | 잔액으로는 이 생성 비용을 결제할 수 없습니다. 요금은 청구되지 않았습니다. | 결제 및 청구에서 크레딧을 충전하거나 자동 충전을 켜세요. 자세한 내용은 결제 및 청구 문서를 참조하세요. | 수정한 뒤 다시 제출하세요. |
| 404 | not_found | 엔드포인트 ID, 모델 또는 생성 내역이 존재하지 않거나, 해당 생성이 다른 계정에 속해 있습니다. | ID가 모델 목록에 있는지 확인하세요. | 없음 |
| 409 | conflict | 리소스 상태가 요청과 충돌합니다. 보통 이미 실행 중이거나 완료된 생성을 취소하려 할 때 발생합니다. | 생성 내역을 확인해 현재 상태를 살펴보세요. | 없음 |
| 410 | model_delisted | 이 모델은 더 이상 제공되지 않습니다. 제출되거나 요금이 청구된 항목은 없으며, 이전 생성 내역은 계속 확인할 수 있습니다. | 모델 목록에서 다른 모델을 선택하세요. | 없음 |
| 429 | rate_limited | 초당 요청이 너무 많거나, 시작을 기다리는 생성 대기열이 가득 찼습니다. | 백오프 후 재시도하세요. 자세한 내용: 요청 제한. | 백오프 재시도 |
| 500 | internal_error | 시스템에서 문제가 발생했습니다. | 재시도 간격을 점차 늘리세요. 계속되면 요청 시각을 적어 고객 지원에 문의하세요. | 백오프 재시도 |
| 501 | direct_upload_unavailable | 이 배포 환경에서는 직접 업로드(사전 서명 URL)를 사용할 수 없습니다. | 대신 multipart/form-data 형식으로 업로드하세요. 자세한 내용: 업로드. | 없음 |
| 502 | — | 요청이 API에 도달하기 전에 게이트웨이 또는 네트워크 오류가 발생했습니다(드문 경우). 업스트림 모델 오류는 HTTP 오류로 표시되지 않습니다. 생성 내역에 오류 사유가 표시됩니다. | 백오프를 적용해 재시도하세요. 요금은 청구되지 않았습니다. | 백오프 재시도 |
| 503 | model_unavailable | 이 모델은 일시적으로 새 생성을 받지 않습니다. | 나중에 다시 시도하거나 모델을 바꾸세요. 상태 페이지에서 상태를 확인하세요. | 백오프 재시도 |
| 503 | pricing_unavailable | 잠시 가격 정보를 불러오지 못해 요청 가격을 계산하지 않았습니다. 요청은 제출되거나 과금되지 않았습니다. | 몇 초 후 같은 Idempotency-Key로 같은 요청을 다시 시도하세요(응답에 Retry-After 헤더가 포함됩니다). | 백오프 재시도 |
| 503 | fallback_price_declined | 일반 용량을 사용할 수 없어 현재는 공식 가격 이하의 긴급 요금만 이용할 수 있습니다. 요청에는 allow_fallback_price: false 값이 설정되어 있어 작업이 거부되었으며 요금은 청구되지 않았습니다. 오류 메시지에서 일반 요금과 긴급 요금을 모두 확인할 수 있습니다. | 일반 요금으로 다시 시도하거나 기본값인 allow_fallback_price: true로 설정해 긴급 요금을 허용하세요. | 백오프 재시도 |
| 400 | max_cost_exceeded | 긴급 요금을 포함한 가격이 max_cost_usd 상한을 초과해 작업이 거부되었습니다. 요금은 청구되지 않았습니다. | 해상도나 길이를 낮추거나 가격을 확인한 뒤 max_cost_usd 상한을 높이세요. | 수정한 뒤 다시 제출하세요. |
생성 과정에서 발생하는 오류#
HTTP 오류는 요청 자체가 거부되어 요금이 청구되지 않았다는 뜻입니다. 접수 후 실패하는 생성 작업은 다릅니다. 작업이 바로 실패하더라도 API 호출(POST /run)은 HTTP 202 응답으로 성공합니다. 생성 객체에는 status 필드 값(failed)과 error 객체가 포함됩니다. 보류 금액은 자동 환불됩니다. 같은 객체는 GET /generations, 웹훅 페이로드, MCP 도구 결과에서도 확인할 수 있습니다.
{
"status": "failed",
"error": {
"code": "generation_failed",
"message": "ByteDance's safety review for Seedance 2.0 Fast rejected this request (the prompt or an input file).",
"reason": "content_blocked",
"hint": "Describe the character's look instead of naming a copyrighted character or real person, keep it non-explicit, or try another model.",
"retryable": false,
"refunded": true,
"detail": "요청이 콘텐츠 안전성 검토를 통과하지 못했습니다. 프롬프트를 수정한 뒤 다시 시도하세요."
}
}code는 출시 이후 문서화된 네 가지 상위 분류 값 중 하나입니다. 정확한 실패 이유는 reason에서 확인할 수 있습니다. 이를 기준으로 분기하고 사용자에게 message 및 hint 값을 표시하세요. retryable 필드는 같은 요청을 다시 보내도 되는지를 나타냅니다. 필드 변경 정보(param)와 제공자 원문(detail)도 확인하세요.
| error.reason | error.code | 발생한 문제 | 해결 방법 | 그대로 다시 보내도 되나요? |
|---|---|---|---|---|
content_blocked | generation_failed | 모델 측 안전 필터가 프롬프트, 입력 파일 또는 생성 결과 (예: 저작권 캐릭터 이름, 실존 인물, 노골적인 콘텐츠)를 거부했습니다. 메시지에서 어느 모델의 필터인지 확인할 수 있습니다. | 프롬프트를 바꾸거나 (캐릭터, 브랜드, 실존 인물을 직접 언급하는 대신 외형을 설명), 다른 입력 파일을 사용하거나 다른 모델을 시도하세요. 그대로 다시 보내면 같은 이유로 실패합니다. | 아니요 — 먼저 수정하세요 |
invalid_input | generation_failed | 모델이 입력 또는 설정을 받을 수 없습니다. 파일 크기가 너무 크거나, 형식이 지원되지 않거나, 크기나 화면 비율이 맞지 않거나, 길이가 너무 길거나, 지원하지 않는 값일 수 있습니다. 가능한 경우 param에 문제가 있는 필드가 표시됩니다. | 표시된 필드를 수정하세요 (모델 API 페이지의 제한 참고). 또는 다른 모델을 사용하세요. | 아니요 — 먼저 수정하세요 |
input_unreachable | generation_failed | 입력 URL에서 파일을 다운로드할 수 없습니다. 로그인이 필요하거나, 서명된 링크가 만료되었거나, 파일로 직접 연결되지 않거나, 호스트가 요청을 거부했을 수 있습니다. 모델 서비스에서 파일을 가져오지 못하면 보통 저희가 직접 다운로드해 사본으로 재시도하므로, 이 메시지는 대개 파일을 완전히 다운로드하지 못했다는 뜻입니다. | 공개된 직접 파일 URL을 사용하거나 POST /api/v1/uploads로 파일을 업로드한 뒤 다시 제출하세요. | 아니요 — 먼저 수정하세요 |
upstream_unavailable | upstream_error | 모델 제공 서비스가 중단되었거나 과부하 상태이거나 용량에 도달해 모든 사용 가능 경로를 시도했습니다. | 1–2분 뒤 다시 시도하거나 다른 모델을 사용하세요. | 예 |
upstream_error | upstream_error | 모든 사용 가능 경로를 시도한 뒤 생성 중 모델 서비스 내부 오류가 발생했습니다. | 다시 시도하세요. 이런 오류는 대개 일시적입니다. 계속 실패하면 다른 모델을 사용하세요. | 예 |
timeout | timeout | 모델 제공 서비스의 제한 또는 당사의 40분 제한 시간 안에 생성이 완료되지 않았습니다. | 다시 시도하세요. 짧거나 해상도가 낮은 클립이 더 빨리 완료됩니다. | 예 |
output_unavailable | upstream_error | 모델은 작업을 완료했지만 사용할 수 있는 파일을 반환하지 않았거나 결과를 다운로드해 저장하지 못했습니다. | Retry. | 예 |
model_unavailable | model_unavailable | 생성이 진행되는 동안 모델 또는 해당 모드의 제공이 중단되었습니다. | 다른 모델을 선택하거나 나중에 다시 시도하세요. | 아니요 — 먼저 수정하세요 |
canceled | upstream_error | 제공자가 작업을 완료 전에 취소하거나 만료했습니다. (직접 취소한 생성은 상태가 canceled이며 오류가 없습니다.) | Retry. | 예 |
generation_failed | generation_failed | 모델이 결과 없이 중단되었으며 이유도 제공하지 않았습니다. | 한 번 더 시도하세요. 다시 실패하면 프롬프트나 입력을 단순화하거나 다른 모델을 사용하세요. | 예 |
요청으로 인해 발생한 실패(content_blocked, invalid_input에 대한 파일 거부, input_unreachable)는 다른 제공자에서 재시도하지 않습니다. 다시 요청해도 거부되기 때문입니다. 장애와 제공자 오류는 같은 가격으로 이용할 수 있는 모든 경로에서 우선 재시도합니다. 자세한 내용: 실패 및 환불.
모델 서비스에서 입력 파일을 다운로드하지 못했다고 하면 보통 저희가 직접 다운로드를 시도합니다. 가능하면 저희가 호스팅하는 사본으로 작업을 다시 시도합니다. 가격은 그대로이며, 사용자가 변경할 것은 없습니다. 생성 내역에는 전송한 URL이 계속 표시되고, 사본은 각 생성 작업의 보관 기간에 따라 함께 삭제됩니다. 따라서 input_unreachable은 파일을 전혀 다운로드하지 못했다는 뜻일 가능성이 큽니다. 로그인이 필요하거나, 서명된 링크가 만료되었거나, 파일로 직접 연결되지 않거나, 호스트가 요청을 거부한 경우일 수 있습니다. 이미지 최대 30MB, 동영상 200MB, 오디오 50MB까지 복사하며, 더 크거나 읽을 수 없는 파일은 invalid_input 오류로 처리됩니다.
안전하게 재시도하기#
지수 백오프와 지터를 적용해 429, 500, 502, 503을 재시도하세요. 그 외 4xx 오류는 수정 없이 재시도하지 마세요. 같은 방식으로 다시 실패합니다.
생성 제출은 멱등성을 보장하지 않습니다.
성공한 POST /run/{endpoint} 요청마다 새 생성 작업이 만들어지고 요금이 청구됩니다. 네트워크 시간 초과로 제출이 접수되었는지 알 수 없다면 다시 보내기 전에 최근 생성 내역에서 최근 생성 내역을 확인하세요.
const RETRYABLE = new Set([429, 500, 502, 503]);
export async function postWithRetry(url, headers, body, attempts = 5) {
let resp;
for (let attempt = 0; attempt < attempts; attempt++) {
resp = await fetch(url, { method: "POST", headers, body: JSON.stringify(body) });
if (!RETRYABLE.has(resp.status)) break;
// exponential backoff with jitter: ~1s, 2s, 4s, 8s…
await new Promise((r) => setTimeout(r, Math.min(2 ** attempt, 30) * 1000 + Math.random() * 1000));
}
const data = await resp.json();
if (!resp.ok) throw new Error(`${resp.status} ${data.error.code}: ${data.error.message}`);
return data;
}유효성 검사 예시#
유효성 검사 메시지에는 필드명과 허용 값이 표시되므로 개발자에게 그대로 보여줄 수 있습니다:
[
{
"error": {
"code": "invalid_request",
"message": "\"prompt\" is required",
"param": "prompt"
}
},
{
"error": {
"code": "invalid_request",
"message": "Unknown parameter \"resoluton\"",
"param": "resoluton"
}
},
{
"error": {
"code": "not_found",
"message": "Unknown endpoint \"bytedance/seedance-2.0-fast/text-to-gif\". See GET /api/v1/models."
}
}
]실행하지 않고도 요청 가격을 확인할 수 있습니다. POST /api/v1/quote/{endpoint}이 가격에 영향을 주는 필드를 검증하고 비용을 반환합니다. API 레퍼런스를 참고하세요.
궁금한 점이나 빠진 내용이 있나요? support@cheapergen.com로 이메일을 보내 주세요. 모든 의견을 읽고 있습니다.