Seedance의 오류는 두 계층으로 나뉩니다. 하나는 401, 403, 400, 429 같은 HTTP 요청 실패이고, 다른 하나는 요청은 성공했지만 작업이 최종적으로 failed 또는 expired 되는 경우입니다. 제출 인터페이스가 200을 반환했다고 성공으로 판단하는 것이 비디오 API 연동에서 가장 흔한 오판입니다. 올바른 확인 순서는 먼저 HTTP 상태 코드를 보고, 다음으로 작업 상태와 error 필드를 보고, 마지막에야 프롬프트를 바꾸거나 모델을 교체하는 것입니다.
이 글은 오류 유형별로 원인, 처리 순서, 재사용 가능한 오류 처리 코드를 제공합니다. 인터페이스 경로, 상태 명칭, 할당량 기준은 버전에 따라 조정될 수 있으므로 실제 사이트 문서와 공식 문서를 기준으로 하십시오.
안내: Seedance, Doubao, Volcano Engine은 바이트댄스 및 그 계열사의 상표입니다. 본 문서는 기술 설명 목적이며 어떤 소속이나 협찬 관계도 뜻하지 않습니다. 예시의 경로와 필드는 설명용일 뿐이므로 호출 전 문서를 확인하십시오.
1. 먼저 요청 실패와 작업 실패를 구분하기
| 현상 | 계층 | 흔한 원인 | 처리 |
|---|---|---|---|
| 401 | HTTP | Key 누락, 형식 오류, 만료 | 인증 수정 |
| 403 | HTTP | 권한 부족, 모델 미개통, 리전 제한 | 권한과 개통 상태 확인 |
| 400 | HTTP | 파라미터 범위 초과, 모델명 오류, 소재 접근 불가 | 문서와 대조해 파라미터 수정 |
| 429 | HTTP | 동시성, 속도, 할당량 제한 | 백오프, 동시성 축소 |
| 5xx | HTTP | 업스트림 과부하 또는 일시 장애 | 백오프, 경로 전환 |
| failed | 작업 | 검수 거부, 소재 문제, 생성 이상 | error를 읽고 소재 수정 |
| expired | 작업 | 시간 초과로 미완료 또는 결과 미회수 | 작업 재생성, 기한 확인 |
| 다운로드 실패 | 결과 | URL 만료, 인증, 네트워크 | 재획득 또는 이전 저장 |
로그에는 최소한 시간, HTTP 상태 코드, 오류 코드, 모델명, task_id, 업무 번호, 소요 시간을 기록합니다. 전체 Key를 기록하지 말고 서명이 포함된 결과 URL도 원문 그대로 출력하지 마십시오.
2. 401과 403: 먼저 요청 헤더와 Key 확인
401은 신원 검증 실패를 뜻하며 보통 모델 성능이나 잔액과 무관합니다. 순서대로 확인하십시오.
Authorization이Bearer <key>형식인지. Volcano Ark는 API Key를 Bearer Token으로 사용합니다.- Key에 공백, 줄바꿈, 불필요한 따옴표가 있는지, 환경 변수가 실제로 로드되었는지.
- Base URL과 Key가 같은 플랫폼, 같은 리전에 속하는지.
- Key가 삭제, 교체, 중지되었거나 프로젝트 권한이 부족하지 않은지.
- 리버스 프록시가
Authorization헤더를 덮어쓰거나 버리지 않는지.
403은 권한 문제가 더 많습니다. 모델 미개통, 계정에 해당 모델 권한 없음, 리전 또는 규정 준수 제한입니다. 401은 새 Key로 최소 요청을 보내면 빠르게 진단할 수 있지만, 403은 콘솔의 개통 상태와 계정 권한을 확인해야 하며 코드를 고쳐도 보통 효과가 없습니다.
3. 429: 속도 제한, 동시성과 할당량
429는 요청이 속도, 동시성 또는 할당량 제한을 넘었다는 뜻입니다. 흔한 발동 장면은 다음과 같습니다.
- 여러 서비스가 Key 하나를 공유해 서로 할당량을 잠식합니다.
- 배치 작업이 정시에 동시에 시작해 트래픽 폭주를 만듭니다.
- 재시도에 백오프가 없어 실패할수록 더 많이 보내 재시도 폭풍이 됩니다.
- 장시간 작업이 동시성을 가득 채워 짧은 작업도 대기열에 못 들어갑니다.
처리 원칙은 지수 백오프와 무작위 지터, 동시성 축소, 프로젝트별 Key 분리, 제출 시간 분산입니다. 백오프 없이 루프 재시도하지 마십시오. 429 기간에 계속 고빈도로 제출하면 속도 제한이 지속 장애로 번집니다. 업무상 높은 동시성이 정말 필요하면 할당량 상한을 미리 확인하고 대체 경로를 준비하십시오.
4. 400: 대부분 파라미터 오류
400은 보통 오류 메시지에서 바로 위치를 찾을 수 있습니다. 중점 확인 항목은 다음과 같습니다.
- 모델명과 버전: 문서에서 복사하고 기억으로 접미사를 조합하지 마십시오.
- 길이: 모델마다 범위가 다릅니다. 예를 들어 2.5는 4~30초, 2.0은 4~15초, 1.0은 2~12초입니다.
- 해상도와 비율: 해당 모델이 4K, 21:9, adaptive를 지원하는지 확인합니다.
- 참조 소재 수: 2.5는 최대 이미지 30장, 비디오 10편, 오디오 10편이며 2.0의 상한은 더 낮습니다.
- 이미지 또는 비디오 URL: 플랫폼이 접근할 수 있어야 하며 내부망 주소나 로그인이 필요한 링크는 안 됩니다.
- 편집 또는 이어쓰기 작업: 일부 모드는 자동 길이만 허용하고 초를 직접 입력할 수 없습니다.
- 오디오 입력: 일부 버전은 순수 오디오 입력을 지원하지 않습니다.
가장 시간을 아끼는 방법은 문서 예시 요청 본문으로 최소 재현을 만든 뒤 자신의 파라미터를 하나씩 다시 추가하는 것입니다. 항목을 추가할 때마다 한 번씩 테스트하면 구체적인 필드를 찾을 수 있습니다.
5. 작업 failed와 expired
제출 성공은 작업이 대기열에 들어갔다는 뜻일 뿐입니다. 조회 결과의 흔한 상태는 queued, running, succeeded, failed, expired입니다. failed는 error 필드를 봐야 합니다. 콘텐츠 검수 거부, 입력 소재 규정 위반, 참조 이미지 읽기 실패, 생성 과정 이상은 처리 방식이 완전히 다릅니다.
expired는 작업이 실행 기한을 넘겨 완료되지 않았거나 결과를 회수하지 못한 경우입니다. 문서에서 execution_expires_after를 설정할 수 있고 기본값은 48시간이며 작업 ID는 7일간 보존됩니다. 기한이 지난 뒤 계속 폴링해도 복구되지 않고 작업을 다시 만들어야 합니다. 콜백을 쓸 때는 콜백을 5초 내에 확인하지 않으면 3회 재시도되므로 콜백 인터페이스가 멱등이어야 합니다.
검수 거부는 단어 몇 개를 바꿔 반복 우회하지 마십시오. 먼저 소재 라이선스, 인물 초상, 브랜드 요소, 콘텐츠 정책을 확인한 뒤 재제출 여부를 결정하십시오.
6. 시간 초과와 폴링
시간 초과는 세 계층으로 나눕니다. 클라이언트 연결 시간 초과, 단일 조회 시간 초과, 작업 전체 deadline입니다. 제출 인터페이스는 보통 task_id를 빠르게 반환하고 실제 시간은 대기열과 렌더링에서 소모됩니다. HTTP 클라이언트 타임아웃이 너무 짧으면 정상 제출을 실패로 오판하고, 너무 길면 스레드 풀을 붙잡습니다.
폴링은 5초에서 시작해 15~30초까지 점진적으로 백오프하고 전체 deadline을 설정하는 것이 좋습니다. 콜백을 지원하면 콜백을 우선 사용하고 폴링은 안전망으로 두십시오. 시간 초과 후 즉시 재제출하지 말고 업무 번호나 멱등 키로 이미 실행 중인 작업이 있는지 확인하십시오.
7. 결과 다운로드 실패
작업 성공이 파일 확보를 뜻하지는 않습니다. video_url은 서명이 포함된 임시 주소인 경우가 많고 유효 기간이 있습니다. 바로 저장하지 않고 몇 시간 뒤 다운로드하면 403이나 404가 흔합니다. 처리 방식은 다음과 같습니다.
- 성공 즉시 자체 객체 스토리지로 스트리밍 다운로드합니다.
- 다운로드 실패는 별도로 재시도하고 생성 작업을 다시 만들지 않습니다.
- Content-Type, 파일 크기, 재생 가능성을 검증합니다.
- 로그에 전체 서명 URL을 출력하지 않습니다.
8. 예제: 백오프와 멱등을 포함한 오류 처리
아래 코드는 제출, 폴링, 다운로드를 분리해 처리하고 일시적 오류에만 재시도합니다. 경로와 필드는 사이트 문서를 기준으로 하십시오.
import os, time, random, requests
BASE = os.environ["SEEDANCE_BASE_URL"].rstrip("/")
HEADERS = {
"Authorization": f"Bearer {os.environ['SEEDANCE_API_KEY']}",
"Content-Type": "application/json",
}
def submit(payload, max_retries=5):
# 호출 전에 업무 번호로 작업 테이블을 조회하고 기존 task_id가 있으면 재사용합니다.
for attempt in range(max_retries):
try:
r = requests.post(
f"{BASE}/contents/generations/tasks",
headers=HEADERS, json=payload, timeout=(5, 30),
)
except requests.Timeout:
time.sleep(min(2 ** attempt, 20) + random.random())
continue
if r.status_code == 429 or r.status_code >= 500:
time.sleep(min(2 ** attempt, 20) + random.random())
continue
if r.status_code >= 400:
raise RuntimeError(f"{r.status_code}: {r.text[:500]}")
return r.json()["id"]
raise RuntimeError("submit retries exhausted")
def wait(task_id, deadline_seconds=900):
deadline = time.time() + deadline_seconds
delay = 5
while time.time() < deadline:
r = requests.get(
f"{BASE}/contents/generations/tasks/{task_id}",
headers=HEADERS, timeout=(5, 30),
)
if r.status_code == 429 or r.status_code >= 500:
time.sleep(delay)
delay = min(delay * 1.5, 30)
continue
r.raise_for_status()
data = r.json()
if data["status"] == "succeeded":
return data["content"]["video_url"]
if data["status"] in ("failed", "expired"):
raise RuntimeError(f"{data['status']}: {data.get('error')}")
time.sleep(delay)
delay = min(delay * 1.5, 30)
raise TimeoutError(task_id)
def download(url, path):
with requests.get(url, stream=True, timeout=(5, 120)) as r:
r.raise_for_status()
with open(path, "wb") as f:
for chunk in r.iter_content(1024 * 256):
f.write(chunk)
이 코드는 의도적으로 400, 401, 403을 재시도하지 않습니다. 파라미터 오류와 인증 오류는 몇 번을 재시도해도 성공하지 않고 로그와 비용만 키웁니다.
9. 재시도, 멱등과 지원 체크리스트
재시도는 일시적 오류에만 적합합니다. 429, 5xx, 연결 시간 초과입니다. 400, 401, 403과 검수 거부는 자동 재시도하지 않아야 합니다. 멱등의 핵심은 재시도 횟수가 아니라 업무 번호입니다. 제출 전에 작업 테이블을 조회하고, 제출 후 task_id를 저장하며, 중복 요청은 기존 작업을 재사용합니다.
서비스 제공자에게 문의하기 전에 다음 체크리스트로 정보를 정리하십시오.
- 발생 시간, HTTP 상태 코드, 오류 코드, 전체 오류 메시지를 기록하고 민감 정보를 마스킹합니다.
- 모델명, 길이, 해상도, 비율, 참조 소재 수를 기록합니다.
- task_id, 업무 번호, 제출 횟수, 폴링 횟수를 기록합니다.
- 사용자 개인정보와 키를 제거한 최소 재현 요청을 보관합니다.
- 요청 실패와 작업 failed 또는 expired를 구분합니다.
- 소재 URL이 공개 접근 가능한지, 응답이 지나치게 느리지 않은지 확인합니다.
- 할당량, 동시성, 계정 권한을 확인해 429와 403을 배제합니다.
- 요청 ID와 재현 시간을 첨부해 조사를 요청합니다.
자주 묻는 질문
제출 인터페이스가 200을 반환했는데 왜 작업이 실패하나요?
200은 작업 생성 성공만 뜻합니다. 비디오 생성은 비동기이므로 최종 상태는 조회 결과의 status와 error 필드로 확인해야 합니다.
429는 잔액 부족인가요?
아닙니다. 429는 보통 속도, 동시성, 할당량 제한을 뜻하며 잔액 문제는 다른 오류로 반환될 수 있습니다. 먼저 동시성, 공유 Key, 재시도 전략을 확인하십시오.
작업 failed 후 바로 재시도할 수 있나요?
먼저 error를 읽으십시오. 검수 거부와 소재 규정 위반은 그대로 재시도하면 보통 다시 실패합니다. 네트워크나 업스트림 장애는 백오프 재시도가 가능합니다. 재시도 전에 업무 번호로 멱등 확인을 하십시오.
폴링은 얼마나 자주 해야 하나요?
5초에서 시작해 15~30초까지 점진적으로 백오프하고 전체 타임아웃을 설정하십시오. 콜백을 지원하면 콜백을 우선하고 폴링은 안전망으로 사용하십시오.
결과 URL이 열리지 않으면 어떻게 하나요?
대부분 유효 기간 만료나 서명 무효입니다. 성공 즉시 객체 스토리지로 이전 저장해야 하며 이미 만료되었다면 작업을 다시 조회하거나 재생성하고 오래된 링크를 반복 요청하지 마십시오.
다음 단계
먼저 ZSQH AI Gateway에서 테스트 Key를 만들고 401, 429, 파라미터 오류, 작업 실패를 각각 한 번씩 리허설해 경보와 재시도 전략이 작동하는지 확인하십시오. 모델 가용성과 과금 방식은 모델 가격을 기준으로 합니다.
관련 글:
