> For the complete documentation index, see [llms.txt](https://docs.amigo.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.amigo.ai/developer-guide/classic-api/sdks/sdk-error-handling.md).

# Error Handling

This guide covers error handling when using the Amigo SDK in Python or TypeScript. Both SDKs include built-in retry logic for transient failures and provide full error handling for all API operations.

## Error Types

Both SDKs provide typed error handling for different types of failures:

{% tabs %}
{% tab title="Python" %}

```python
from amigo_sdk.errors import (
    AmigoError,           # Base error class
    AuthenticationError,   # Invalid credentials (401)
    ForbiddenError,       # Permission denied (403)
    NotFoundError,        # Resource not found (404)
    BadRequestError,      # Invalid request (400)
    ValidationError,      # Request validation failed (422)
    ConflictError,        # Resource conflict (409)
    RateLimitError,       # Rate limit (429)
    ServerError,          # Server error (5xx)
    ServiceUnavailableError,  # Service unavailable (503)
)
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import {
  AmigoError,           // Base error class
  AuthenticationError,  // Invalid credentials (401)
  PermissionError,      // Permission denied (403)
  NotFoundError,        // Resource not found (404)
  BadRequestError,      // Invalid request (400)
  ValidationError,      // Request validation failed (422)
  ConflictError,        // Resource conflict (409)
  RateLimitError,       // Rate limit (429)
  ServerError,          // Server error (5xx)
  ServiceUnavailableError, // Service unavailable (503)
  NetworkError,         // Network/connection failures
} from '@amigo-ai/sdk'
```

{% endtab %}
{% endtabs %}

Both SDKs map HTTP status codes to typed errors, including `RateLimitError` for 429 responses; any failure without a dedicated class is raised through the base `AmigoError`. The SDKs differ on connection failures: the TypeScript SDK wraps them in a typed `NetworkError`, while the Python SDK lets the underlying `httpx` exceptions (for example `httpx.ConnectError` and `httpx.TimeoutException`) propagate.

## Basic Error Handling

{% tabs %}
{% tab title="Python" %}

```python
from amigo_sdk import AmigoClient
from amigo_sdk.errors import (
    AuthenticationError,
    NotFoundError,
    BadRequestError,
    ValidationError,
    ConflictError,
    RateLimitError,
    AmigoError
)

def handle_errors_example():
    try:
        with AmigoClient(
            api_key="your-api-key",
            api_key_id="your-api-key-id",
            user_id="your-user-id",
            organization_id="your-org-id"
        ) as client:
            # Your API calls here
            org = client.organizations.get()
            print(f"Organization: {org.org_name}")
            
    except AuthenticationError as e:
        print(f"Authentication failed: {e}")
        # Handle invalid credentials. Check API keys.
        
    except NotFoundError as e:
        print(f"Resource not found: {e}")
        # Handle missing resources. Check IDs.
        
    except BadRequestError as e:
        print(f"Bad request: {e}")
        # Handle invalid request format
        
    except ValidationError as e:
        print(f"Validation failed: {e}")
        # Handle request validation errors
        
    except ConflictError as e:
        print(f"Conflict: {e}")
        # Handle resource conflicts (often recoverable)
        
    except RateLimitError as e:
        print(f"Rate limited: {e}")
        # Handle 429 rate limiting. GET requests (and POSTs with a
        # Retry-After header) are retried automatically before this is raised.
        
    except AmigoError as e:
        print(f"Amigo API error: {e}")
        # Handle any other Amigo-specific errors
        
    except Exception as e:
        print(f"Unexpected error: {e}")
        # Handle unexpected errors
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import {
  AmigoClient,
  AmigoError,
  AuthenticationError,
  NotFoundError,
  BadRequestError,
  ValidationError,
  ConflictError,
  RateLimitError,
  NetworkError,
} from '@amigo-ai/sdk'

async function handleErrorsExample(): Promise<void> {
  const client = new AmigoClient({
    apiKey: 'your-api-key',
    apiKeyId: 'your-api-key-id',
    userId: 'your-user-id',
    orgId: 'your-org-id'
  })

  try {
    // Your API calls here
    const org = await client.organizations.getOrganization()
    console.log(`Organization: ${org.org_name}`)
    
  } catch (error) {
    if (error instanceof AuthenticationError) {
      console.log(`Authentication failed: ${error.message}`)
      // Handle invalid credentials. Check API keys.
      
    } else if (error instanceof NotFoundError) {
      console.log(`Resource not found: ${error.message}`)
      // Handle missing resources. Check IDs.
      
    } else if (error instanceof BadRequestError) {
      console.log(`Bad request: ${error.message}`)
      // Handle invalid request format
      
    } else if (error instanceof ValidationError) {
      console.log(`Validation failed: ${error.message}`)
      // Handle request validation errors
      
    } else if (error instanceof ConflictError) {
      console.log(`Conflict: ${error.message}`)
      // Handle resource conflicts (often recoverable)
      
    } else if (error instanceof RateLimitError) {
      console.log(`Rate limited: ${error.message}`)
      // Handle 429 rate limiting. GET requests (and POSTs with a
      // Retry-After header) are retried automatically before this is thrown.
      
    } else if (error instanceof NetworkError) {
      console.log(`Network error: ${error.message}`)
      // Handle connection failures. GET requests are retried automatically.
      
    } else if (error instanceof AmigoError) {
      console.log(`Amigo API error: ${error.message}`)
      // Handle any other Amigo-specific errors
      
    } else {
      console.log(`Unexpected error:`, error)
      // Handle unexpected errors
    }
  }
}
```

{% endtab %}
{% endtabs %}

## Stream Error Handling

When processing streaming responses, handle errors within the stream. See also [Conversations: Interact](/developer-guide/classic-api/core-api/conversations/conversations-interact.md).

{% tabs %}
{% tab title="Python" %}

```python
from amigo_sdk import AmigoClient
from amigo_sdk.models import (
    ConversationCreateConversationRequest,
    CreateConversationParametersQuery,
)

def handle_stream_errors():
    with AmigoClient() as client:
        try:
            events = client.conversations.create_conversation(
                ConversationCreateConversationRequest(service_id="your-service-id"),
                CreateConversationParametersQuery(response_format="text"),
            )
            
            for event in events:
                event_data = event.model_dump(mode="json")
                
                # Check for error events in the stream
                if event_data.get("type") == "error":
                    description = event_data.get("error_description", "Unknown error")
                    status = event_data.get("http_error_code")
                    print(f"Stream error ({status}): {description}")
                    break
                    
                # Process normal events
                print(f"Event: {event_data.get('type')}")
                
        except Exception as e:
            print(f"Stream processing error: {e}")
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
async function handleStreamErrors(): Promise<void> {
  const client = new AmigoClient({ ...config })

  try {
    const events = await client.conversations.createConversation({
      body: { service_id: 'your-service-id' },
      query: { response_format: 'text' },
    })

    for await (const event of events) {
      // Check for error events in the stream
      if (event.type === 'error') {
        console.log(`Stream error (${event.http_error_code}): ${event.error_description}`)
        break
      }

      // Process normal events
      console.log(`Event: ${event.type}`)
    }
  } catch (error) {
    console.log(`Stream processing error:`, error)
  }
}
```

{% endtab %}
{% endtabs %}

## Built-in Retry Logic

Both SDKs share the same default retry policy for transient failures:

| Setting                  | Default                                                                   |
| ------------------------ | ------------------------------------------------------------------------- |
| **Max attempts**         | 3                                                                         |
| **Backoff**              | Exponential with full jitter, 250ms base, 30s max delay per attempt       |
| **Retried statuses**     | 408, 429, 500, 502, 503, 504                                              |
| **Retried methods**      | `GET`, plus `POST` when the response is a 429 with a `Retry-After` header |
| **`Retry-After` header** | Honored (numeric or HTTP-date), capped at the max delay                   |

{% hint style="info" %}
**GET requests are retried; POST requests generally are not.** By default, only `GET` requests are retried on network errors and retryable statuses. `POST` requests (including conversation creation and interactions) are retried only for a 429 response that carries a `Retry-After` header; other `POST` failures surface immediately so you can decide whether to resend.
{% endhint %}

You can tune this policy: the TypeScript client accepts a `retry` option (`maxAttempts`, `backoffBaseMs`, `maxDelayMs`, `retryOnStatus`, `retryOnMethods`), and the Python client accepts `retry_max_attempts`, `retry_backoff_base`, `retry_max_delay_seconds`, `retry_on_status`, and `retry_on_methods` keyword arguments.

### Retry Flow Diagram

```mermaid
%%{init: {"flowchart": {"useMaxWidth": true, "nodeSpacing": 30, "rankSpacing": 40}, "theme": "base", "themeVariables": {"primaryColor": "#D4E2E7", "primaryTextColor": "#100F0F", "primaryBorderColor": "#083241", "lineColor": "#575452", "textColor": "#100F0F", "clusterBkg": "#F1EAE7", "clusterBorder": "#D7D2D0"}}}%%
flowchart TB
    Start[API Request] --> Try[Execute Request]
    Try --> Check{Success?}

    Check -->|2xx| Success[Return Response]
    Check -->|Failure| Retryable{Retryable?}

    Retryable -->|"GET: network error<br/>or 408/429/5xx"| Retry{Retry Count < Max?}
    Retryable -->|"POST: 429 with<br/>Retry-After header"| Retry
    Retryable -->|"Anything else"| Fail[Throw Error]

    Retry -->|Yes| Wait[Backoff or Retry-After Delay]
    Wait --> Try
    Retry -->|No| Fail

    style Success fill:#DDE3DB,stroke:#2c3827,color:#100F0F,stroke-width:2px
    style Fail fill:#F0DDD9,stroke:#AA412A,color:#100F0F,stroke-width:2px
    style Wait fill:#F0DDD9,stroke:#AA412A,color:#100F0F,stroke-width:2px
    style Try fill:#D4E2E7,stroke:#083241,color:#100F0F,stroke-width:2px
```

## Best Practices

1. **Handle specific error types.** Don't just catch generic exceptions.
2. **Log errors with context.** Include relevant IDs and operation details.
3. **Check for stream error events.** Monitor the `error` event type in streams.
4. **Trust built-in retries.** Let the SDK retry transient `GET` failures automatically; handle failed `POST` requests explicitly.
5. **Graceful degradation.** Provide fallback behavior when possible.
6. **Monitor error patterns.** Track error rates and types in production.

## Next Steps

With proper error handling in place, you're ready to:

* [**Work with conversations**](/developer-guide/classic-api/core-api/conversations/conversations-interact.md)**.** Streaming and reliable handlers.
* [**Manage users**](/developer-guide/classic-api/core-api/users.md)**.** User management with error handling.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.amigo.ai/developer-guide/classic-api/sdks/sdk-error-handling.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
