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

# 오류 처리

> ARouter 오류 응답 형식과 애플리케이션에서 처리하는 방법을 이해합니다.

## 오류 형식

모든 오류는 일관된 JSON 형식을 따릅니다：

```json theme={null}
{
  "error": {
    "message": "사람이 읽을 수 있는 오류 설명",
    "type": "error_type"
  }
}
```

## 오류 유형

| HTTP 상태 | 오류 유형                                  | 설명                                 |
| ------- | -------------------------------------- | ---------------------------------- |
| `400`   | `invalid_request_error`                | 요청 본문 형식이 잘못되었거나 필수 필드가 누락됨        |
| `401`   | `authentication_error`                 | API key 누락, 유효하지 않음 또는 만료됨         |
| `402`   | `payment_required`                     | 크레딧 부족 — 계정을 충전하세요                 |
| `403`   | `permission_error` / `forbidden_error` | 이 키는 요청한 제공업체 또는 리소스에 대한 접근 권한이 없음 |
| `413`   | `invalid_request_error`                | 요청 본문이 너무 큼 (최대 10 MB)             |
| `429`   | `rate_limit_error`                     | 속도 제한 초과 (RPM 또는 일일 한도)            |
| `502`   | `server_error`                         | 업스트림 제공업체 요청 실패                    |
| `503`   | `server_error`                         | 요청한 모델에 사용 가능한 제공업체 없음             |

## 일반적인 오류 및 해결책

### 401 — 잘못된 API Key

```json theme={null}
{
  "error": {
    "message": "invalid api key",
    "type": "authentication_error"
  }
}
```

**해결책：** API key가 올바르고 취소되지 않았는지 확인하세요.

### 403 — 제공업체 접근 불가

```json theme={null}
{
  "error": {
    "message": "provider 'anthropic' is not allowed for this API key",
    "type": "forbidden_error"
  }
}
```

**해결책：** API key에 `allowed_providers` 제한이 있습니다. 다른 키를 사용하거나 관리 API를 통해 허용된 제공업체를 업데이트하세요.

### 429 — 속도 제한

```json theme={null}
{
  "error": {
    "message": "rate limit exceeded",
    "type": "rate_limit_error"
  }
}
```

**해결책：** 지수 백오프를 구현하세요. 키의 속도 제한을 높이는 것을 고려하세요.

### 502 — 업스트림 실패

```json theme={null}
{
  "error": {
    "message": "upstream request failed",
    "type": "server_error"
  }
}
```

**해결책：** LLM 제공업체가 오류를 반환했거나 연결할 수 없습니다. ARouter는 자동으로 키 페일오버를 처리하지만 제공업체 자체에 문제가 있을 수 있습니다. 다시 시도하거나 다른 제공업체로 전환하세요.

## 코드에서 오류 처리

<Tabs>
  <Tab title="Python (OpenAI)">
    ```python theme={null}
    from openai import OpenAI, APIStatusError, APIConnectionError

    client = OpenAI(
        base_url="https://api.arouter.ai/v1",
        api_key="lr_live_xxxx",
    )

    try:
        response = client.chat.completions.create(
            model="openai/gpt-5.4",
            messages=[{"role": "user", "content": "Hello!"}],
        )
    except APIStatusError as e:
        if e.status_code == 401:
            print("Invalid API key")
        elif e.status_code == 402:
            print("Insufficient credits — top up your account")
        elif e.status_code == 429:
            print("Rate limited, backing off...")
        elif e.status_code in (502, 503):
            print("Provider error, retrying...")
        else:
            print(f"API error {e.status_code}: {e.message}")
    except APIConnectionError:
        print("Network error — check your connection")
    ```
  </Tab>

  <Tab title="Node.js (OpenAI)">
    ```typescript theme={null}
    import OpenAI from "openai";

    const client = new OpenAI({
      baseURL: "https://api.arouter.ai/v1",
      apiKey: "lr_live_xxxx",
    });

    try {
      const response = await client.chat.completions.create({
        model: "openai/gpt-5.4",
        messages: [{ role: "user", content: "Hello!" }],
      });
    } catch (error) {
      if (error instanceof OpenAI.APIError) {
        switch (error.status) {
          case 401: console.error("Invalid API key"); break;
          case 402: console.error("Insufficient credits"); break;
          case 429: console.error("Rate limited"); break;
          case 502:
          case 503: console.error("Provider error"); break;
          default:  console.error(`Error ${error.status}: ${error.message}`);
        }
      }
    }
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    resp, err := client.ChatCompletion(ctx, req)
    if err != nil {
        var apiErr *arouter.APIError
        if errors.As(err, &apiErr) {
            switch apiErr.StatusCode {
            case 401:
                log.Fatal("Invalid API key")
            case 429:
                log.Println("Rate limited, backing off...")
                time.Sleep(time.Second)
            case 502:
                log.Println("Provider error, retrying...")
            }
        } else {
            log.Fatal("Network error:", err)
        }
    }
    ```
  </Tab>
</Tabs>

## 재시도 전략

프로덕션 애플리케이션에서는 다음을 권장합니다：

1. **429와 502에는 지수 백오프로 재시도**
2. **400, 401, 403은 재시도하지 않기** — 영구적인 오류
3. **최대 재시도 횟수 설정** (예: 3회)
4. **멀티 모델 라우팅 고려** — 하나의 모델이 요청을 처리할 수 없을 경우 `models`와 `route`를 통해 순서가 있는 후보 목록 전송

```python theme={null}
import time
from openai import OpenAI, APIStatusError

def call_with_retry(client, request, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.chat.completions.create(**request)
        except APIStatusError as e:
            if e.status_code in (429, 502, 503) and attempt < max_retries - 1:
                time.sleep(2 ** attempt)
                continue
            raise
```

## 스트리밍 중 오류 처리

스트리밍(`stream: true`)을 사용할 때 오류 동작은 발생 시점에 따라 다릅니다：

* **Token이 전송되기 전** — ARouter는 비 200 상태 코드와 함께 표준 HTTP 오류 응답을 반환합니다. 비스트리밍 오류와 동일하게 처리하세요.
* **Token이 전송된 후** — HTTP 상태는 이미 200 OK입니다. 오류는 스트림 본문의 SSE 이벤트로 전달됩니다.

스트림 도중 오류는 다음과 같습니다：

```json theme={null}
{
  "id": "chatcmpl-xxx",
  "object": "chat.completion.chunk",
  "error": {
    "code": "server_error",
    "message": "Provider disconnected unexpectedly"
  },
  "choices": [
    { "index": 0, "delta": { "content": "" }, "finish_reason": "error" }
  ]
}
```

각 청크의 `finish_reason`을 확인하세요. `"error"`이면 스트림이 비정상적으로 종료된 것입니다.

```typescript theme={null}
for await (const chunk of stream) {
  // Check for mid-stream error
  if ("error" in chunk) {
    console.error(`Stream error: ${(chunk as any).error.message}`);
    break;
  }
  if (chunk.choices[0]?.finish_reason === "error") {
    console.error("Stream terminated due to an error");
    break;
  }
  const content = chunk.choices[0]?.delta?.content;
  if (content) process.stdout.write(content);
}
```

완전한 스트리밍 오류 처리 예제는 [스트리밍 가이드](/ko/guides/streaming)를 참조하세요.
