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

# Errors & retries

> Every SDK raises the same Bedrock-named exceptions, with the same retryability semantics you already handle.

Every SDK raises the **same Bedrock-named exceptions**, so your existing
error-handling paths — including catch-and-backoff logic keyed on
`ThrottlingException` — port over unchanged. Every server-derived error carries
`message`, `statusCode`, `errorCode`, `requestId`
(quote it to support), `retryAfterSeconds`, and the raw response body.

| Exception                       | Retried automatically | Typical cause                                                     |
| ------------------------------- | --------------------- | ----------------------------------------------------------------- |
| `ValidationException`           | no                    | Malformed request (HTTP 400)                                      |
| `AccessDeniedException`         | no                    | Bad or missing key; model not allowed (401/403)                   |
| `ResourceNotFoundException`     | no                    | Unknown model (404)                                               |
| `ServiceQuotaExceededException` | no                    | Budget / usage cap exhausted (402, or 429 with `BUDGET_EXCEEDED`) |
| `ThrottlingException`           | yes                   | Rate limited (429) — honors `Retry-After`                         |
| `ModelTimeoutException`         | yes                   | Model timeout (408)                                               |
| `ModelErrorException`           | no                    | Model-level failure                                               |
| `ModelStreamErrorException`     | no                    | Stream interrupted after the first byte                           |
| `ServiceUnavailableException`   | yes                   | Upstream unavailable (502/503/504)                                |
| `InternalServerException`       | yes                   | Gateway error (500)                                               |
| `MissingApiKeyError`            | —                     | No key resolved; fails before any network call                    |
| `SdkError`                      | network only          | Connection / decode failure                                       |

<CodeGroup>
  ```python Python theme={"dark"}
  from audacity.exceptions import SdkError

  try:
      response = client.converse(modelId="gpt-5.4-mini", messages=[…])
  except client.exceptions.ThrottlingException as e:
      print(f"Rate limited: {e.message}, retry after {e.retry_after_seconds}s")
  except client.exceptions.AccessDeniedException as e:
      print(f"Auth error [{e.status_code}]: {e.message}")
  except client.exceptions.ServiceQuotaExceededException as e:
      print(f"Budget exhausted: {e.message}")
  except SdkError as e:
      print(f"Network/decode error: {e.message}")
  ```

  ```typescript TypeScript theme={"dark"}
  import {
    AccessDeniedException,
    ThrottlingException,
    MissingApiKeyError,
    AudacityError,
  } from "@audacity/sdk";

  try {
    const res = await client.send(new ConverseCommand({ /* … */ }));
  } catch (err) {
    if (err instanceof MissingApiKeyError) {
      console.error("No API key configured");
    } else if (err instanceof AccessDeniedException) {
      console.error("Auth failed:", err.message, "requestId:", err.requestId);
    } else if (err instanceof ThrottlingException) {
      console.error("Rate limited. Retry after:", err.retryAfterSeconds, "s");
    } else if (err instanceof AudacityError) {
      // All SDK errors are instances of AudacityError
      console.error(err.name, err.statusCode, err.errorCode);
    }
  }
  ```

  ```go Go theme={"dark"}
  _, err := client.Converse(ctx, input)
  switch {
  case err == nil:
      // success
  case errors.Is(err, &types.MissingAPIKeyError{}):
      log.Fatal("set AUDACITY_API_KEY")
  default:
      var throttle *types.ThrottlingException
      var quota *types.ServiceQuotaExceededException
      var accessDenied *types.AccessDeniedException

      switch {
      case errors.As(err, &throttle):
          fmt.Printf("rate limited (retry-after=%v)\n", throttle.RetryAfterSeconds)
      case errors.As(err, &quota):
          fmt.Println("budget exhausted — will not retry")
      case errors.As(err, &accessDenied):
          fmt.Println("check your API key")
      default:
          log.Fatal(err)
      }
  }
  ```

  ```java Java theme={"dark"}
  try {
      ConverseResponse resp = client.converse(request -> request
          .modelId("gpt-5.4-mini")
          .messages(Message.builder()
              .role(ConversationRole.USER)
              .content(ContentBlock.fromText("Hi"))
              .build()));
  } catch (ThrottlingException e) {
      System.err.printf("Rate limited. Retry-After: %s s%n", e.retryAfterSeconds());
  } catch (AccessDeniedException e) {
      System.err.println("Auth failed: " + e.getMessage());
  } catch (ServiceQuotaExceededException e) {
      System.err.println("Budget or quota exceeded — not retried");
  } catch (AudacityException e) {
      System.err.printf("SDK error [%d] %s%n", e.statusCode(), e.getMessage());
  }
  ```

  ```rust Rust theme={"dark"}
  use audacity_sdk::Error;

  match client.converse().model_id("m").messages(msg).send().await {
      Ok(resp) => { /* use resp */ }
      Err(Error::Throttling(d)) => {
          eprintln!("Rate limited (retry after {:?}s): {}", d.retry_after_seconds, d.message);
      }
      Err(Error::AccessDenied(d)) => {
          eprintln!("Access denied [{}]: {}", d.error_code.unwrap_or_default(), d.message);
      }
      Err(Error::MissingApiKey) => {
          eprintln!("Set AUDACITY_API_KEY");
      }
      Err(e) => eprintln!("Other error: {e}"),
  }
  ```
</CodeGroup>

## Retry policy (Bedrock standard-mode analog)

* Attempts = `maxRetries + 1`; the default is 2 retries (3 total attempts).
* Retried: network errors, HTTP 429, 500, 502, 503, 504, and 408 — with full-jitter exponential backoff capped at 20 s, honoring any `Retry-After` header.
* Never retried: auth failures, validation errors, unknown models, and **budget exhaustion** (`BUDGET_EXCEEDED`) — spending errors must never silently retry.
* Streaming: retries apply only until response headers arrive. Once the first byte of the stream is consumed, a drop surfaces as `ModelStreamErrorException` — a partial generation is never silently replayed.
