> ## Documentation Index
> Fetch the complete documentation index at: https://docs.supertoneapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 오류 처리

> Supertone API가 반환하는 HTTP 상태 코드와 원인, 그리고 프로덕션 코드에서의 올바른 대응 방법을 안내합니다.

<Note>
  이 문서는 영어 원문을 기반으로 자동 번역되었습니다. 표현이 어색하거나 모호한 부분이 있을 수 있으니, 정확한 내용은 [영어 원문](/en/docs/production/error-handling)을 함께 확인해 주세요.
</Note>

Supertone API는 표준 HTTP 상태 코드를 사용합니다. 오류 발생 시 문제를 설명하는 JSON 본문이 반환되며, SDK는 이를 타입이 지정된 예외로 노출하므로 손쉽게 캐치하여 처리할 수 있습니다.

## 상태 코드 레퍼런스

| Status                       | 의미                | 가장 흔한 원인                                                                           |
| ---------------------------- | ----------------- | ---------------------------------------------------------------------------------- |
| `200 OK`                     | 성공                | —                                                                                  |
| `400 Bad Request`            | 잘못된 형식의 요청        | 필수 필드 누락, 잘못된 enum 값, `text` 300자 초과(raw API에 한함), `voice_settings` 값이 허용 범위를 벗어남. |
| `401 Unauthorized`           | 인증 실패             | `x-sup-api-key`가 누락되었거나 올바르지 않음.                                                   |
| `402 Payment Required`       | 크레딧 부족            | 크레딧 잔액이 0이거나 요청을 처리하기에 부족함.                                                        |
| `403 Forbidden`              | 권한 없음             | 다른 계정 소유의 커스텀 보이스를 호출하거나, 권한이 없는 키를 사용한 경우.                                        |
| `404 Not Found`              | 리소스 없음            | 잘못된 `voice_id`, 엔드포인트 경로 오타.                                                       |
| `408 Request Timeout`        | 서버가 시간 내에 처리하지 못함 | 일시적인 문제 — 재시도해 주세요.                                                                |
| `413 Payload Too Large`      | 업로드 용량 초과         | 보이스 클로닝 오디오가 3 MB를 초과함.                                                            |
| `415 Unsupported Media Type` | 잘못된 업로드 포맷        | 보이스 클로닝 오디오가 WAV/MP3 형식이 아님.                                                       |
| `429 Too Many Requests`      | 요청 수 제한 도달        | [요청 수 제한](/ko/docs/production/rate-limits)을 참고해 주세요.                               |
| `500 Internal Server Error`  | 서버 측 문제           | 보통 일시적 — 백오프와 함께 재시도해 주세요.                                                         |

## SDK 오류 클래스

두 SDK 모두 각 상태 코드를 타입이 지정된 오류 클래스로 매핑합니다. 문자열을 파싱하는 것보다 특정 클래스를 캐치하는 방식이 더 명확합니다.

<Tabs>
  <Tab title="Python">
    ```python theme={"dark"}
    from supertone import errors

    try:
        response = client.text_to_speech.create_speech(...)
    except errors.BadRequestErrorResponse as e:
        # 400 — fix the request
        log.error("Bad request: %s", e.message)
    except errors.UnauthorizedErrorResponse:
        # 401 — re-check the API key
        rotate_key_and_retry()
    except errors.PaymentRequiredErrorResponse:
        # 402 — alert the user / billing system
        notify_low_credits()
    except errors.ForbiddenErrorResponse:
        # 403 — wrong voice for this account
        fallback_to_preset_voice()
    except errors.TooManyRequestsErrorResponse:
        # 429 — back off and retry
        backoff_and_retry()
    except errors.InternalServerErrorResponse:
        # 500 — retry transient failure
        retry_with_backoff()
    except errors.SupertoneError as e:
        # Any other API-level error
        log.exception("Supertone API error: %s", e.message)
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"dark"}
    import * as errors from "@supertone/supertone/models/errors";

    try {
      const response = await client.textToSpeech.createSpeech({ /* ... */ });
    } catch (err) {
      if (err instanceof errors.BadRequestErrorResponse) {
        console.error("Bad request:", err.message);
      } else if (err instanceof errors.UnauthorizedErrorResponse) {
        rotateKeyAndRetry();
      } else if (err instanceof errors.PaymentRequiredErrorResponse) {
        notifyLowCredits();
      } else if (err instanceof errors.ForbiddenErrorResponse) {
        fallbackToPresetVoice();
      } else if (err instanceof errors.TooManyRequestsErrorResponse) {
        await backoffAndRetry();
      } else if (err instanceof errors.InternalServerErrorResponse) {
        await retryWithBackoff();
      } else if (err instanceof errors.SupertoneError) {
        console.error(`HTTP ${err.statusCode}: ${err.message}`);
      } else {
        throw err;
      }
    }
    ```
  </Tab>
</Tabs>

모든 SDK 오류는 다음 속성을 노출합니다.

| Property                       | 설명                   |
| ------------------------------ | -------------------- |
| `message`                      | 서버에서 제공하는 오류 메시지입니다. |
| `status_code` / `statusCode`   | HTTP 상태 코드입니다.       |
| `headers`                      | 응답 헤더입니다.            |
| `body`                         | 응답 본문의 원본 문자열입니다.    |
| `raw_response` / `rawResponse` | 내부 HTTP 응답 객체입니다.    |

## 오류별 대응 방법

### 4xx — 클라이언트 측

| Code  | 권장 대응                                                                              |
| ----- | ---------------------------------------------------------------------------------- |
| `400` | 재시도하지 마세요 — 요청 본문을 로그로 남기고 스키마 불일치를 수정합니다.                                         |
| `401` | 키 값, 콘솔에서의 키 활성화 상태, 그리고 프록시에 의해 `x-sup-api-key` 헤더가 제거되지 않았는지 확인하세요.              |
| `402` | 사용자에게 "크레딧 부족" 메시지를 노출하고 결제 페이지로 안내한 뒤 워크플로를 중단하세요. 크레딧을 충전하지 않고 재시도하면 동일하게 실패합니다. |
| `403` | 해당 보이스가 이 계정 소유인지 확인하세요. 커스텀 보이스는 계정 단위로 범위가 제한됩니다.                                |
| `404` | `GET /v1/voices` 또는 `GET /v1/custom-voices`를 호출해 `voice_id`가 실제로 존재하는지 확인하세요.      |
| `413` | 업로드 용량을 줄여 주세요 — 모노로 재인코딩하거나, 비트레이트를 낮추거나, 3 MB 미만으로 잘라 주세요.                       |
| `415` | 업로드 파일을 WAV 또는 MP3로 재인코딩하세요.                                                       |
| `429` | [재시도와 백오프](/ko/docs/production/retries-and-backoff)를 참고해 주세요.                      |

### 5xx — 서버 측

| Code  | 권장 대응                                                                           |
| ----- | ------------------------------------------------------------------------------- |
| `408` | 재시도해 주세요 — 보통 일시적 문제입니다.                                                        |
| `500` | 지수 백오프와 함께 재시도해 주세요. 5분 이상 지속될 경우 [고객 지원](/ko/docs/resources/support)에 문의해 주세요. |

## 자주 발생하는 케이스

* POST 요청에 **`Content-Type: application/json` 누락** 시 `400` 발생.
* 복사/붙여넣기 과정에서 흔히 발생하는 **API Key의 앞뒤 공백**으로 인해 `401` 발생.
* 다른 계정의 **커스텀 보이스 호출** 시, ID를 "알고 있다"고 해도 `403` 발생.
* raw API에 **`text`를 300자 초과**로 전송 시 `400` 발생. 더 긴 텍스트는 SDK를 사용해 자동 청크 분할하세요.

## 관련 문서

<CardGroup cols={2}>
  <Card title="재시도와 백오프" icon="rotate" href="/ko/docs/production/retries-and-backoff">
    `429`와 `5xx`에 대한 올바른 재시도 정책을 확인하세요.
  </Card>

  <Card title="요청 수 제한" icon="gauge" href="/ko/docs/production/rate-limits">
    계정 티어별 제한을 확인하세요.
  </Card>
</CardGroup>
