Skip to content

Distiller Client Exceptions

NOTE: The following exceptions are available for the SDK version 1.24.0 and later releases.

The AI Refinery SDK includes a robust set of custom exceptions designed to help you handle errors gracefully. Whether dealing with network instability, authentication issues, or database logging errors, these exceptions provide granular control over your application's flow.

All exceptions are exported from air.distiller.exceptions.

Exception Hierarchy

Understanding the inheritance structure allows you to catch specific errors or broader categories of failures.

DistillerClientError (Base Exception)
├── AuthenticationError
├── ProjectCreationError
├── ProjectDownloadError
├── ConnectionError
│   ├── UserAlreadyConnectedError
│   ├── ConnectionTimeoutError
│   ├── ConnectionClosedError
│   └── WebSocketError
│       ├── WebSocketSendError
│       └── WebSocketReceiveError
└── DatabaseError
    ├── HistoryRetrievalError
    └── ChatLoggingError

Base Exception

DistillerClientError

The base class for all Distiller-related errors. You can catch this exception to handle any error raised by the Distiller client.

Attributes:

  • message (str): A human-readable error description.
  • error_code (str): A unique string code (e.g., distiller.client.error).
  • status (HTTPStatus): An associated HTTP status code (e.g., 500, 404, 401).
  • extra (dict): A dictionary containing context-specific debugging information (e.g., project names, specific reason codes).

Authentication & Configuration Errors

AuthenticationError

Status: 401 UNAUTHORIZED

Raised when the API key provided to the client is invalid, expired, or fails validation against the server.

ProjectCreationError

Status: 400 BAD REQUEST

Raised when client.create_project() fails. This usually indicates an issue with the project payload or server-side validation.

  • Extra Info: Contains status_code, error_message, and the project name.

ProjectDownloadError

Status: 404 NOT FOUND

Raised when client.download_project() fails to retrieve a configuration. This often happens if the project name or version does not exist.

  • Extra Info: Contains project and project_version.

Connection & Runtime Errors

These errors occur during the WebSocket lifecycle (connection, interaction, and disconnection).

ConnectionError

Status: 503 SERVICE UNAUTHORIZED

The base class for all network and connectivity issues.

UserAlreadyConnectedError

Status: 409 CONFLICT

Raised when attempting to connect with a UUID that is already active in another session. The Distiller enforces a single active connection per UUID.

  • Mitigation: Ensure previous sessions are closed or use a unique UUID for the new session.

ConnectionTimeoutError

Status: 504 GATEWAY TIMEOUT

Raised in two scenarios:

  1. Ping Monitor: The client has not received a heartbeat (PING) from the server within the expected interval.
  2. Idle Timeout: The server closed the connection because the session was idle for too long.

ConnectionClosedError

Raised when the WebSocket connection is closed unexpectedly or forcefully, but not due to a timeout or conflict.

WebSocketError

Base class for protocol-level errors.

  • WebSocketSendError: Failed to send a message to the server.
  • WebSocketReceiveError: Failed to receive or parse a message from the server.

Data & History Errors

HistoryRetrievalError

Raised when client.retrieve_history() fails to fetch past chat logs from the Postgres database.

  • Extra Info: Contains specific account, project, and uuid details.

ChatLoggingError

Raised when the client fails to log a conversation turn to the database (in _log_chat).

  • Extra Info: Contains the table_name and role that failed to insert.

Usage Examples

1. Robust Connection Handling

This example demonstrates how to differentiate between a user conflict (which might require user intervention) and a timeout (which might trigger an automatic retry).

import asyncio
from air import AsyncAIRefinery
from air.distiller.exceptions import (
    UserAlreadyConnectedError,
    ConnectionTimeoutError,
    ConnectionError
)

client = AsyncAIRefinery(api_key="...")

async def connect_session():
    try:
        async with client.distiller(project="my_agent", uuid="user_123") as dc:
            await dc.query(query="Hello Agent")

    except UserAlreadyConnectedError as e:
        print(f"Session Conflict: {e.message}")
        print("Please close your other tab or wait a moment.")

    except ConnectionTimeoutError as e:
        print(f"Connection timed out. Last ping received: {e.extra.get('last_ping')}")
        print("Attempting reconnection...")
        # Add logic to retry connection

    except ConnectionError as e:
        print(f"Network error ({e.status}): {e.message}")

2. Debugging with extra Info

When developing, the extra dictionary provides valuable context without needing to parse the error message string.

from air.distiller.exceptions import ProjectCreationError

try:
    client.distiller.create_project(
        project="invalid_project_name@@", 
        config_path="config.yaml"
    )
except ProjectCreationError as e:
    print("Creation Failed!")
    # Access structured data for logging
    print(f"Server responded with: {e.extra.get('status_code')}")
    print(f"Server error message: {e.extra.get('error_message')}")