에러 처리

공통 에러 envelope, 상태 코드, 부분 실패 처리.

모든 에러 응답은 같은 형태입니다 — OpenAI 스타일 슈퍼셋. type+code는 변하지 않는 머신 값이므로 분기에 사용하세요. message는 사람이 읽는 용도이며 바뀔 수 있습니다. param은 문제가 된 필드명(있을 때, 없으면 null).

{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_error",
    "message": "template is invalid",
    "param": "template",
    "retryable": false
  }
}

상태 코드

HTTPtypecode의미retryable
401authentication_errorunauthorized인증 실패 (키 없음/무효)아니오
403permission_errorforbidden스코프 초과 (권한 없는 호출)아니오
404invalid_request_errornot_found리소스 없음아니오
409invalid_request_erroridempotency_conflict멱등 키 충돌 (같은 Idempotency-Key, 다른 본문)아니오
409invalid_request_erroridempotency_in_progress같은 멱등 키의 리포트 생성이 아직 진행 중예 (잠시 후 재시도)
409invalid_request_errorslot_already_succeeded이미 성공한 슬롯에 새 원본을 replace=true 없이 등록아니오
409invalid_request_errorreport_discarded폐기된 리포트 변경 시도아니오
409invalid_request_errorreport_purge_pending테스트 리포트 정리 작업이 진행 중
409invalid_request_errorcontent_unavailable_for_legacy버전 콘텐츠 계약 도입 이전 리포트에서 /content 조회아니오
422invalid_request_errorvalidation_error검증 실패 (필드 오류)아니오
429rate_limit_errorrate_limited게이트웨이에서 일시적으로 요청이 거부됨 (현재 고정 한도는 강제하지 않으나 게이트웨이에서 발생 가능)예 (지수 백오프)
503api_errordiscard_incomplete접근 회수 후 폐기 처리가 완료되지 않음
5xxapi_errorinternal_error서버 오류예 (지수 백오프)

retryable: true인 경우에만 재시도하세요. POST /reports를 재시도할 때 Idempotency-Key와 요청 본문을 그대로 유지하면 중복 생성을 막습니다. discard_incomplete는 접근이 이미 회수된 상태이므로 같은 DELETE /reports/{id} 요청을 다시 호출하세요.

param: "asset_id" 인 422

POST /reports/{id}/images에서 paramasset_idvalidation_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}/content202를 반환하는 것은 HTTP 오류가 아닙니다. 본문의 statuspending이면 Retry-After 뒤 재조회하고, failed이면 error.code == "content_source_incomplete"를 확인한 뒤 report 템플릿의 실패한 필수 슬롯 (트레드와 사이드월 포함)을 복구하세요.

폐기된 리포트는 복구하지 않습니다

폐기 후 upload-targets, images, analyze, publish, 공유 링크 재발급 등 모든 변경 요청은 409 report_discarded로 거절됩니다. 새 검사는 새 external_inspection_idPOST /reports를 호출해 서버에서 새 report_id를 발급받으세요.

다음 단계

On this page