오류와 재시도
오류 응답 형식
오류는 항상 아래 envelope로 옵니다.
{
"error": {
"code": "RATE_LIMITED",
"message": "Request limit exceeded",
"retryable": true,
"request_id": "0b7b8f0e-0000-4000-8000-000000000010",
"details": {"reset_at": "2026-10-05T09:01:00+00:00"}
}
}| 필드 | 설명 |
|---|---|
code | 기계가 읽는 오류 코드. 분기에는 message가 아니라 이 값을 쓰세요. |
message | 사람이 읽는 영어 설명. 바뀔 수 있습니다. |
retryable | 같은 요청을 다시 보내면 성공할 수 있는지 서버가 알려 주는 값 |
request_id | 이 요청의 ID. 문의할 때 알려 주세요. |
details | 코드에 따른 추가 정보. 예를 들어 reset_at, state_url |
- 모든 응답에
X-Request-Id헤더가 있고 본문의request_id와 같습니다. 서버가 요청마다 새로 발급합니다. - 429와 일부 503에는
Retry-After(초) 헤더가 있습니다. - 모든 응답은
Cache-Control: no-store입니다. - 요청에
X-Correlation-Id를 넣으면 호출자 쪽 추적에 쓸 수 있습니다. 서버가 만드는request_id와는 별개입니다. - 접수에 성공(
202)한 Turn이나 Run이 나중에 실패하는 것은 HTTP 오류가 아닙니다. 상세 조회 응답의error.code로 확인합니다.
재시도 원칙
retryable이true인 오류와 네트워크 오류(연결 끊김, 시간 초과)만 재시도합니다.Retry-After가 있으면 그 시간만큼 기다립니다. 없으면 지수 백오프에 무작위 지연을 더합니다.- 생성, 입력, 취소, 업로드 요청은 재시도할 때 같은
Idempotency-Key와 같은 본문을 보냅니다. 이미 접수된 요청이면 같은 receipt가 돌아오므로 중복 실행이 없습니다. retryable이false인 오류는 같은 요청을 그대로 다시 보내도 같은 결과입니다. 요청을 고치거나 원인을 해결하세요.- 응답을 못 받은 요청을 새 키로 다시 보내면 같은 일이 두 번 실행될 수 있습니다. 재시도에는 키를 바꾸지 마세요.
- 재시도 횟수와 총 대기 시간에 상한을 두세요.
def post_with_retry(url: str, key: str, body: dict, attempts: int = 5) -> httpx.Response:
for attempt in range(attempts):
try:
response = httpx.post(url, headers={**AUTH, "Idempotency-Key": key}, json=body, timeout=30)
except httpx.TransportError:
response = None
if response is not None and response.status_code < 400:
return response
try:
error = response.json().get("error", {}) if response is not None else {}
except ValueError: # a proxy may answer 502/503/504 without the JSON envelope
error = {"retryable": response.status_code in (429, 502, 503, 504)}
if response is not None and not error.get("retryable"):
response.raise_for_status()
delay = float(response.headers.get("Retry-After", 0)) if response is not None else 0
time.sleep(delay or min(30, 0.5 * 2 ** attempt))
raise RuntimeError("gave up after retries")HTTP 상태별 의미
| 상태 | 의미 |
|---|---|
| 200, 201 | 성공. 조회와 close는 200, 파일 업로드는 201 |
| 202 | 접수됨. 결과는 나중에 조회합니다. |
| 400 | 형식이 잘못된 cursor, 상관 ID 같은 요청 오류 |
| 401 | 키 문제. 폐기된 키는 재시도해도 소용없습니다. |
| 402 | SU 잔액 또는 예산 문제 |
| 403 | scope나 권한 부족 |
| 404 | 없거나 권한 밖인 자원. 존재 여부를 알려 주지 않습니다. |
| 409 | 현재 상태와 충돌. 예를 들어 이미 실행 중이거나 키가 충돌한 경우 |
| 410 | 보존 기간이 지나 내용이 없는 경우 |
| 413, 415 | 크기 한도 초과, 지원하지 않는 형식 |
| 422 | 값이 허용 범위나 schema를 벗어남 |
| 429 | 호출 속도 또는 동시 실행 한도 |
| 500, 503 | 서버 쪽 문제. retryable과 Retry-After를 따르세요. |
오류 코드 표
아래 표는 호출자가 받을 수 있는 공개 코드입니다. HTTP 상태가 -인 코드는 HTTP 응답이 아니라 Turn이나 Run 상세의 error.code로만 보이며, 이때 retryable은 false입니다. retryable은 같은 요청을 다시 보내는 것에 대한 값이라 끝난 실행에서는 늘 false입니다. 새 실행을 만들어도 되는지는 아래 “실패한 실행 뒤 다음 행동” 표를 따르세요. SESSION_SANDBOX_UNAVAILABLE처럼 HTTP 응답과 실행 상세에 모두 쓰이는 코드는 한 행에 HTTP 응답일 때의 상태와 retryable을 적고, 실행 상세에서 보일 때의 뜻을 의미 칸에 함께 적었습니다. 내부에서만 쓰는 코드는 싣지 않았습니다. 새 코드가 추가될 수 있으니 모르는 코드는 retryable과 HTTP 상태로 처리하세요.
| 코드 | HTTP 상태 | retryable | 의미 |
|---|---|---|---|
NOT_FOUND | 404 | false | 없거나 권한 밖인 자원입니다. 허용되지 않은 Project에서는 모든 경로가 이 코드입니다. |
HTTP_ERROR | 4xx | false | 경로나 메서드가 맞지 않는 등 라우팅 단계의 거절입니다. |
INVALID_API_KEY | 401 | false | 키가 없거나 형식이 틀렸거나 폐기, 만료되었습니다. |
INSUFFICIENT_SCOPE | 403 | false | 키에 이 작업의 scope가 없습니다. |
ACTING_FORBIDDEN | 403 | false | 대리 접근 헤더나 파라미터는 허용되지 않습니다. |
WORKFLOW_DATA_DENIED | 403 | false | 워크플로가 게시 때 받은 데이터 권한 밖의 데이터를 쓰려 했습니다. |
ACTION_NOT_RESOLVABLE_BY_CALLER | 403 | false | 해당 승인을 해소할 명시적 actions:approve 위임이 없습니다. 업무 준비의 범위 확인 답이면 works:approve 위임이 없는 것입니다. |
ACTION_CONTENT_CHANGED | 409 | false | 승인 내용이 바뀌었습니다. 다시 조회하고 사람의 결정을 받으세요. |
APPROVAL_CONTENT_UNAVAILABLE | 409 | false | 승인 자료를 읽을 수 없거나 READ 권한이 부족합니다. |
APPROVAL_CONTENT_TOO_LARGE | 413 | false | 승인 자료가 100행 또는 256KiB를 초과합니다. |
INVALID_INPUT | 400, 422 | false | 요청 본문이나 입력 형식이 잘못되었습니다. 선언되지 않은 필드도 여기에 해당합니다. |
LIMIT_OUT_OF_RANGE | 422 | false | limits 값이 허용 범위를 벗어났습니다. 상한을 넘는 값을 조용히 줄이지 않습니다. |
INVALID_CURSOR | 400 | false | after나 Last-Event-ID가 이 자원의 cursor가 아니거나 서로 다릅니다. |
INVALID_CORRELATION_ID | 400 | false | X-Correlation-Id 형식이 틀렸습니다. |
INVALID_IDEMPOTENCY_KEY | 422 | false | Idempotency-Key가 없거나 1~256바이트가 아닙니다. |
IDEMPOTENCY_CONFLICT | 409 | false | 같은 Idempotency-Key를 다른 본문이나 다른 파일로 보냈습니다. 새 요청에는 새 키를 쓰세요. |
INPUT_TOO_LARGE | 413 | false | JSON 요청 본문이 64KiB를 넘었습니다. |
FILE_TOO_LARGE | 413 | false | 업로드 파일이 20MiB를 넘었습니다. |
UNSUPPORTED_MEDIA_TYPE | 415 | false | JSON이 아니거나 압축된 본문입니다. |
INVALID_FILENAME | 422 | false | 파일 이름이 비었거나 255바이트를 넘거나, 경로 구분자(슬래시, 역슬래시)나 제어 문자를 담고 있습니다. 업로드 응답으로 받았다면 이름을 고쳐 다시 올리세요. Turn이나 Run 상세의 error.code로 보이면 실행 중 AI 직원이 만든 결과 파일의 이름이 이 규칙에 맞지 않아 실행이 실패한 것이며 이때 retryable은 false입니다. |
UNSUPPORTED_FILE_TYPE | 415 | false | 파일 이름의 확장자와 MIME 형식이 허용된 짝이 아니거나, 내용이 그 형식이 아니거나 안전하지 않습니다(예: 매크로가 든 문서, 암호화된 PDF). 업로드할 때, 또는 Deployment가 받지 않는 형식의 파일을 Turn 입력에 넣었을 때 옵니다. 허용 형식은 한도와 데이터에 있습니다. Turn이나 Run 상세의 error.code로 보이면 실행 중 만든 결과 파일이 허용되지 않는 형식이어서 실행이 실패한 것이며 이때 retryable은 false입니다. |
OUTPUT_SCHEMA_MISMATCH | 422 | false | 값이 발행 때 정한 형식이나 schema와 맞지 않습니다. HTTP 응답으로 받았다면 Run 생성 요청의 input이 워크플로의 input_schema와 맞지 않는 것입니다. Turn이나 Run 상세의 error.code로 보이면 결과가 발행 때 정한 결과 계약과 맞지 않아 실행이 실패한 것이며 이때 retryable은 false입니다. JSON 결과의 schema 불일치는 실행 중 AI 직원에게 먼저 돌려줘 고칠 기회를 주고, 고치지 못한 채 끝나면 이 코드가 됩니다. |
OUTPUT_LIMIT_EXCEEDED | 422 | false | 값이 크기나 개수 한도를 넘었습니다. HTTP 응답으로 받았다면 Run 생성 요청의 input이 JSON 한도(깊이 16, 배열 원소 합 10,000개)를 넘은 것입니다. Turn이나 Run 상세의 error.code로 보이면 결과가 크기나 개수 한도를 넘어 실행이 실패한 것이며 이때 retryable은 false입니다. 조용히 자르지 않고 실패로 닫습니다. |
TOO_MANY_FILES | 422 | false | 입력 파일이 20개를 넘었거나 같은 파일을 중복해서 넣었습니다. |
FILE_EXPIRED | 410 | false | 입력 파일이 아직 준비되지 않았거나 만료되었습니다. 다시 업로드하세요. |
ARTIFACT_EXPIRED | 410 | false | 보존 기간이 지나 결과 파일을 더는 받을 수 없습니다. 결과 목록에서는 그 항목이 availability: EXPIRED와 이 error.code로 보입니다. |
PROJECT_STORAGE_LIMIT | 413 | false | Project 파일 보관량(500MiB) 또는 조직의 남은 용량을 넘었습니다. |
RUN_STORAGE_LIMIT | 413 | false | 한 실행의 파일 합계(100MiB)를 넘었습니다. |
REVISION_CONFLICT | 409 | false | 읽은 뒤 업무나 준비가 바뀌었습니다. 다시 조회해 지금 상태로 보내세요. |
PREPARATION_IN_PROGRESS | 409 | false | 같은 업무의 준비가 이미 진행 중입니다. details.preparation_id의 준비를 조회하세요. |
INVALID_TRANSITION | 409 | false | 준비가 지금 상태에서 이 요청을 받지 않습니다. 예를 들어 끝났거나 확인 기한이 지났습니다. |
QUESTION_NOT_FOUND | 409 | false | 열려 있지 않은 준비 질문입니다. 다시 조회하세요. |
PACKAGE_CHANGED | 409 | false | 사람이 확인한 구성 후보가 바뀌었습니다. 새 질문을 다시 보여 주고 확인받으세요. |
INVALID_SPEC | 422 | false | 업무 명세가 규칙에 맞지 않습니다. details.reason을 보세요. |
INVALID_REQUEST | 422 | false | 업무 준비 요청이 규칙에 맞지 않습니다. details.reason을 보세요. |
SESSION_BUSY | 409 | false | Session에 이미 실행 중인 Turn이 있습니다. 종결을 확인한 뒤 보내세요. |
SESSION_CLOSED | 409 | false | 닫힌 Session은 입력과 파일을 받지 않습니다. |
SESSION_EXPIRED | 410 | false | Session 내용의 보존 기간이 끝났습니다. |
SESSION_UPDATING | 503 | true | Session을 갱신하는 중입니다. 같은 Idempotency-Key로 다시 보내세요. |
SESSION_SANDBOX_UNAVAILABLE | 503 | true | Session 실행 환경을 지금 준비하지 못했습니다. HTTP 응답으로 받았다면 잠시 뒤 다시 시도하세요. Turn이나 Run 상세의 error.code로 보이면 작업을 시작하기 전, 실행 환경 준비 단계가 시간 안에 끝나지 않아 Turn이 실패한 것이며 이때 retryable은 false입니다. 워크플로 단계에서 생기면 Run도 대개 이 코드로 끝납니다. 다만 준비 중 보낸 요청의 결과를 확인하지 못했으면 Run은 사용량이 확인될 때까지 EXTERNAL_EFFECT_UNKNOWN으로 남습니다. 앞선 실행을 기다리다 시간이 다 된 경우는 지금처럼 EXECUTION_TIMEOUT입니다. 요청 내용 문제가 아니니 잠시 뒤 새 실행을 만드세요. |
DEPLOYMENT_UNAVAILABLE | 409 | false | 이 Deployment는 지금 이 작업을 받을 수 없습니다. 상태를 확인하세요. |
WORKFLOW_UNAVAILABLE | 409 | false | 워크플로가 게시되어 있지 않거나 멈춰 있거나, 메일로만 시작할 수 있는 워크플로처럼 API로 시작할 수 없어서 Run 생성(POST /v1/workflows/{workflow_id}/runs)을 받지 않습니다. GET /v1/workflows/{workflow_id}는 이 상태를 오류 응답이 아니라 200 본문의 available: false와 error.code로 알려 줍니다. 실행 중에 워크플로가 멈추면 Run은 CANCELED로 끝나고 Run 상세의 error.code가 이 값입니다. 발행한 사람이 워크플로를 다시 게시하거나 켠 뒤에 시도하세요. |
WORKFLOW_MANAGED_SESSION | 409 | false | 워크플로가 쓰는 내부 Session입니다. Run 경로를 사용하세요. |
RUN_TERMINAL | 409 | false | 이미 결과가 확정된 Run입니다. |
ACTION_EXPIRED | 409 | false | 질문의 답변 기한이 지났습니다. |
ACTION_CANCELED | 409 | false | 실행이 더는 이 질문을 기다리지 않습니다. |
MINIMUM_SU_REQUIRED | 402 | false | 파일 작업에는 계정 잔액이 50 SU 이상이어야 합니다. 50 SU를 차감하는 것은 아닙니다. |
INSUFFICIENT_SU | 402 | false | 사용 가능한 SU가 부족합니다. |
BUDGET_EXCEEDED | 402 | false | 이 실행의 예산이 소진되었거나 닫혔습니다. |
PROJECT_BUDGET_EXCEEDED | 402 | true | Project의 일 또는 월 지출 한도를 넘었습니다. 한도 창이 지나면 다시 가능합니다. |
RATE_LIMITED | 429 | true | 호출 속도 한도를 넘었습니다. Retry-After만큼 기다리세요. |
CONCURRENCY_LIMIT | 429 | true | Project의 동시 실행 한도를 넘었습니다. 실행이 끝난 뒤 다시 시도하세요. |
SOURCE_UNAVAILABLE | 503 | true | 워크플로 입력을 확인할 조직 데이터 저장소에 지금 연결할 수 없어 Run 생성(POST /v1/workflows/{workflow_id}/runs)을 받지 않았습니다. Run은 만들어지지 않았습니다. 잠시 뒤 같은 Idempotency-Key로 다시 보내세요. 계속되면 발행한 사람에게 알리세요. |
BILLING_UNAVAILABLE | 503 | true | 결제 계정을 지금 확인하지 못했습니다. 잠시 뒤 다시 시도하세요. |
ARTIFACT_NOT_READY | 409 | true | 파일이 아직 준비되지 않았습니다. 잠시 뒤 다시 시도하세요. |
ARTIFACT_UNAVAILABLE | 503 | true | 보관된 파일을 지금 읽지 못합니다. 다시 시도하세요. |
STORAGE_UNAVAILABLE | 503 | true | 파일 저장소를 지금 쓸 수 없습니다. 다시 시도하세요. |
EVENT_CURSOR_EXPIRED | 410 | false | 이벤트 cursor가 만료되었습니다. details의 주소로 복구하세요. |
OUTPUT_MANIFEST_UNAVAILABLE | 503 | true | 결과 목록을 지금 읽지 못합니다. 다시 시도하세요. |
INTERNAL_ERROR | 500 | false | 서버 내부 오류입니다. request_id와 함께 문의하세요. |
EXECUTION_TIMEOUT | - | false | Turn이 실행 시간 한도를 넘어 실패했습니다. |
QUEUE_TIMEOUT | - | false | 대기 시간 한도 안에 실행이 시작되지 않아 만료되었습니다. |
RECOVERY_EXHAUSTED | - | false | 서버가 복구를 여러 번 시도했지만 끝내지 못했습니다. |
RUN_DEADLINE_EXCEEDED | - | false | Run 전체 기한에 먼저 닿아 만료되었습니다. |
EXTERNAL_EFFECT_UNKNOWN | - | false | 외부에 쓴 작업의 결과를 확인할 수 없어 멈췄습니다. 결과를 직접 확인하세요. 자동으로 다시 실행하지 않습니다. |
ACTION_REQUIRED_UNSUPPORTED | - | false | 사람의 답이나 승인이 필요해졌지만 현재 API에서 지원하지 않아 실패했습니다. |
CAPABILITY_DENIED | - | false | 발행 때 허용하지 않은 도구나 기능을 쓰려 했습니다. |
SESSION_RESOURCES_NOT_READY | - | false | 발행본이 허용한 도구 중 이 단계에서 아직 지원하지 않는 것을 호출했습니다. |
ARTIFACT_PREPARATION_TIMEOUT | - | false | 결과 파일 준비가 기한을 넘었습니다. |
ARTIFACT_PREPARATION_FAILED | - | false | 결과 파일이 실행이 끝날 때 준비되어 있지 않거나 이미 만료되어 결과로 내지 못했습니다. |
OUTPUT_URL_INVALID | - | false | URL 결과가 공개 HTTPS 주소가 아닙니다. 내부망 주소와 로그인 정보, 서명, 토큰이 든 주소도 거절합니다. |
OUTPUT_CONTAINS_CREDENTIALS | - | false | 결과(값, 파일 이름, 파일 내용, URL)에 키나 비밀번호 같은 자격 증명 패턴이 들어 있어 내보내지 않았습니다. |
OUTPUT_PATH_DENIED | - | false | Session 첨부 폴더 밖의 파일을 결과로 내보내려 했습니다. |
OUTPUT_MISSING | - | false | 발행 때 필수로 정한 결과가 만들어지지 않았습니다. |
EXECUTION_FAILED | - | false | 예기치 않은 오류로 실행이 실패했습니다. |
CANCELED | - | false | 취소 요청으로 실행이 멈췄습니다. |
AUTHORIZATION_REVOKED | - | false | 실행 중 키가 폐기, 만료되었거나 scope, Deployment, 발행본이 쓰는 계정 연결을 쓸 수 없게 되어 취소되었습니다. |
SESSION_DELETED | - | false | Session이 삭제되어 진행 중이던 Turn이 취소되었습니다. |
RUNTIME_CONTEXT_MISMATCH | - | false | Session 상태가 실행할 수 있는 상태와 맞지 않아 실패했습니다. |
USAGE_PENDING | - | false | 앞선 사용량 정산을 확인하지 못해 실행을 이어 가지 못했습니다. |
실패한 실행 뒤 다음 행동
Turn이나 Run이 FAILED, CANCELED, EXPIRED로 끝나면 그 실행은 다시 살아나지 않습니다. 다시 하려면 새 Idempotency-Key로 새 Turn이나 Run을 만듭니다. 응답을 받지 못한 요청을 같은 키로 다시 보내는 위의 재시도와는 다릅니다. 끝난 실행의 키를 그대로 쓰면 끝난 실행의 receipt가 다시 돌아옵니다.
AI 도구가 호출을 대신하는 경우에도 아래 순서를 지키세요. 새 실행은 비용이 다시 듭니다.
| 판단 | 코드 | 다음 행동 |
|---|---|---|
| 잠시 뒤 한 번 더 | QUEUE_TIMEOUT, RECOVERY_EXHAUSTED, EXECUTION_FAILED, USAGE_PENDING, ARTIFACT_PREPARATION_TIMEOUT, ARTIFACT_PREPARATION_FAILED, SESSION_SANDBOX_UNAVAILABLE | 시간을 두고 같은 입력으로 새 실행을 한 번 만듭니다. 같은 코드로 다시 끝나면 멈추고 실행 ID와 함께 문의합니다. |
| 조건을 바꾼 뒤 | EXECUTION_TIMEOUT, RUN_DEADLINE_EXCEEDED, BUDGET_EXCEEDED, INSUFFICIENT_SU | 입력을 줄이거나 나누고, 한도와 데이터의 허용 범위 안에서 기한이나 예산을 늘리거나 SU를 충전한 뒤 새 실행을 만듭니다. 같은 조건으로 바로 다시 보내지 않습니다. |
| 발행 설정 문제 | OUTPUT_SCHEMA_MISMATCH, OUTPUT_LIMIT_EXCEEDED, OUTPUT_MISSING, OUTPUT_PATH_DENIED, CAPABILITY_DENIED, SESSION_RESOURCES_NOT_READY, ACTION_REQUIRED_UNSUPPORTED, WORKFLOW_UNAVAILABLE | 호출자가 고칠 수 없는 경우가 많습니다. 같은 입력을 반복하지 말고 Deployment나 워크플로를 발행한 사람에게 알립니다. 입력 때문에 결과가 한도를 넘었다면 입력을 줄여 새 실행을 만듭니다. WORKFLOW_UNAVAILABLE은 워크플로가 다시 게시되거나 켜진 뒤에만 새 Run을 만듭니다. |
| 결과 내용 확인 | OUTPUT_URL_INVALID, OUTPUT_CONTAINS_CREDENTIALS, INVALID_FILENAME, UNSUPPORTED_FILE_TYPE | AI 직원이 만든 결과가 공개 결과 규칙에 맞지 않았습니다. 입력이 내부 주소, 자격 증명, 허용되지 않는 파일 형식이나 이름을 결과에 넣으라고 요구하지 않았는지 확인하고, 고친 입력으로 새 실행을 한 번 만듭니다. 같은 코드로 다시 끝나면 Deployment나 워크플로를 발행한 사람에게 알립니다. |
| 권한 확인 | AUTHORIZATION_REVOKED | 키, scope, Deployment 상태와 발행한 사람의 계정 연결을 확인합니다. 새 키나 권한을 받거나 연결이 복구된 뒤에만 새 실행을 만듭니다. |
| 새 Session에서 | RUNTIME_CONTEXT_MISMATCH | 새 Session을 만들어 실행합니다. 반복되면 실행 ID와 함께 문의합니다. |
| 다시 실행하지 않음 | EXTERNAL_EFFECT_UNKNOWN | 외부 시스템에 이미 쓰였을 수 있습니다. 그 시스템에서 결과를 직접 확인하기 전에는 새 실행을 만들지 않습니다. |
| 의도한 종료 | CANCELED, SESSION_DELETED | 취소나 삭제를 요청한 쪽의 결정입니다. 그 결정이 바뀐 경우에만 새 실행을 만듭니다. |
표에 없는 코드는 다시 실행하지 말고 멈춘 뒤 사람에게 알리세요.
실행 중 AI 직원이 낸 결과가 공개 결과 규칙에 걸리면 그 실행은 그 자리에서 실패로 끝납니다. 결과 형식 오류 가운데 JSON 결과의 schema 불일치만 실행 중 AI 직원에게 돌려줘 고치게 합니다. 실패하지 않고 일부 결과만 받는 워크플로(PARTIALLY_SUCCEEDED)에서는 실패한 결과 항목의 error.code에 같은 코드가 남습니다.
조직 업무 준비의 실패도 HTTP 오류가 아닙니다. 사유는 준비의 reason_codes에 있고, 다음 행동은 조직 업무 만들기에 있습니다.