에러 처리
공통 에러 envelope, 상태 코드, 부분 실패 처리.
모든 에러 응답은 같은 형태입니다 — OpenAI 스타일 슈퍼셋. type+code는 변하지 않는 머신 값이므로 분기에 사용하세요.
message는 사람이 읽는 용도이며 바뀔 수 있습니다. param은 문제가 된 필드명(있을 때, 없으면 null).
{
"error": {
"type": "invalid_request_error",
"code": "validation_error",
"message": "template is invalid",
"param": "template",
"retryable": false
}
}상태 코드
| HTTP | type | code | 의미 | retryable |
|---|---|---|---|---|
401 | authentication_error | unauthorized | 인증 실패 (키 없음/무효) | 아니오 |
403 | permission_error | forbidden | 스코프 초과 (권한 없는 호출) | 아니오 |
404 | invalid_request_error | not_found | 리소스 없음 | 아니오 |
409 | invalid_request_error | idempotency_conflict | 멱등 키 충돌 (같은 Idempotency-Key, 다른 본문) | 아니오 |
409 | invalid_request_error | idempotency_in_progress | 같은 멱등 키의 리포트 생성이 아직 진행 중 | 예 (잠시 후 재시도) |
409 | invalid_request_error | slot_already_succeeded | 이미 성공한 슬롯에 새 원본을 replace=true 없이 등록 | 아니오 |
409 | invalid_request_error | report_discarded | 폐기된 리포트 변경 시도 | 아니오 |
409 | invalid_request_error | report_purge_pending | 테스트 리포트 정리 작업이 진행 중 | 예 |
409 | invalid_request_error | content_unavailable_for_legacy | 버전 콘텐츠 계약 도입 이전 리포트에서 /content 조회 | 아니오 |
422 | invalid_request_error | validation_error | 검증 실패 (필드 오류) | 아니오 |
429 | rate_limit_error | rate_limited | 게이트웨이에서 일시적으로 요청이 거부됨 (현재 고정 한도는 강제하지 않으나 게이트웨이에서 발생 가능) | 예 (지수 백오프) |
503 | api_error | discard_incomplete | 접근 회수 후 폐기 처리가 완료되지 않음 | 예 |
5xx | api_error | internal_error | 서버 오류 | 예 (지수 백오프) |
retryable: true인 경우에만 재시도하세요. POST /reports를 재시도할 때
Idempotency-Key와 요청 본문을 그대로 유지하면 중복 생성을 막습니다.
discard_incomplete는 접근이 이미 회수된 상태이므로 같은 DELETE /reports/{id} 요청을 다시 호출하세요.
param: "asset_id" 인 422
POST /reports/{id}/images에서 param이 asset_id인 validation_error는 등록 요청 자체가 아니라
그 앞 단계의 업로드에 문제가 있다는 뜻입니다. 업로드(PUT)는 200으로 성공했더라도, 원본이
등록 조건을 만족하지 못하면 이 단계에서 거절됩니다. 대표적인 원인은 Cache-Control TTL이 60초를
초과하거나 값을 판정할 수 없는 경우이며, 허용 값은
이미지 등록 가이드의 업로드 규약에 정리돼 있습니다.
이 경우 같은 asset_id에 원본을 다시 올리지 마시고, POST /reports/{id}/upload-targets로 새 타깃을
발급받아 다시 업로드하세요. 재시도해도 결과는 같습니다(retryable: false).
부분 실패는 에러가 아닙니다
일부 슬롯만 실패하는 경우는 HTTP 에러가 아니라 리포트 상태로 나타납니다.
GET /reports/{id}의 슬롯별 analysis.status와 리포트 analysis_status,
summary_status를 확인하세요.
아래는 오류 판단에 필요한 필드만 표시한 축약 예시입니다.
{
"schema_version": "report.v1",
"analysis_status": "partial",
"summary_status": "partial",
"slots": [
{
"slot_id": "tire.tread.front_left",
"module_type": "tire",
"slot": { "position": "front_left", "image_type": "tread" },
"status": "success",
"analysis": { "status": "success", "error": null }
},
{
"slot_id": "tire.tread.rear_right",
"module_type": "tire",
"slot": { "position": "rear_right", "image_type": "tread" },
"status": "failed",
"analysis": {
"status": "failed",
"result": null,
"error": { "code": "analysis_failed", "retryable": true }
}
}
]
}partial은 공개 게이트를 우회할 수 없습니다. 실패한 슬롯은 POST /reports/{id}/analyze로 재분석할 수 있습니다. 재촬영하려면
POST /reports/{id}/upload-targets로 새 타깃을 발급받고, 새 원본을 PUT한 뒤
새 asset_id와 슬롯을 POST /reports/{id}/images?replace=true에 등록합니다.
현재 슬롯에 이미 적용된 같은 asset_id의 /images 등록 재시도는 멱등이며 중복 분석을 만들지 않습니다.
교체 또는 재분석 중에는 기존 공유 링크가 일시 비공개되고, 성공하면 자동으로 다시 공개됩니다.
GET /reports/{id}/content가 202를 반환하는 것은 HTTP 오류가 아닙니다. 본문의
status가 pending이면 Retry-After 뒤 재조회하고, failed이면
error.code == "content_source_incomplete"를 확인한 뒤 report 템플릿의 실패한 필수 슬롯
(트레드와 사이드월 포함)을 복구하세요.
폐기된 리포트는 복구하지 않습니다
폐기 후 upload-targets, images, analyze, publish, 공유 링크 재발급 등
모든 변경 요청은 409 report_discarded로 거절됩니다. 새 검사는 새
external_inspection_id로 POST /reports를 호출해 서버에서 새 report_id를
발급받으세요.