Documentation Index Fetch the complete documentation index at: https://docs.bread.com.ai/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Bread SDK provides a comprehensive error hierarchy for handling different failure scenarios. All errors inherit from bread.APIError.
Error Hierarchy
All Bread SDK errors inherit from BreadError:
APIConnectionError - Network Issues
Connection failures, timeouts, network errors. Subclasses:
APITimeoutError - Request exceeded timeout threshold
Common causes: Network connectivity, firewall, VPN issues
APIStatusError - HTTP Status Errors
HTTP error responses from the API (4xx, 5xx status codes). Subclasses:
BadRequestError (400) - Invalid request parameters
AuthenticationError (401) - Invalid or missing API key
PermissionDeniedError (403) - Insufficient permissions
NotFoundError (404) - Resource doesn’t exist
ConflictError (409) - Resource conflict (e.g., immutable field change)
UnprocessableEntityError (422) - Validation error
RateLimitError (429) - Too many requests
InternalServerError (≥500) - Server-side error
APIResponseValidationError - Invalid Response
API returned data that doesn’t match expected schema. Common causes: API version mismatch, malformed response
Error Status Codes
400 - BadRequestError Malformed request
401 - AuthenticationError Invalid API key
403 - PermissionDeniedError Insufficient permissions
404 - NotFoundError Resource not found
409 - ConflictError Resource conflict (e.g., immutable field change)
422 - UnprocessableEntityError Invalid entity
429 - RateLimitError Rate limit exceeded
500+ - InternalServerError Server error
Basic Error Handling
import aibread
from aibread import Bread
client = Bread()
try :
repos = client.repo.list()
except aibread.APIConnectionError as e:
print ( "Server could not be reached" )
print (e.__cause__) # Underlying exception
except aibread.RateLimitError as e:
print ( "Rate limited - back off" )
except aibread.AuthenticationError as e:
print ( "Invalid API key" )
except aibread.APIStatusError as e:
print ( f "Status: { e.status_code } " )
print ( f "Response: { e.response } " )
Specific Error Handling
Authentication Errors
import aibread
from aibread import Bread
try :
client = Bread()
repos = client.repo.list()
except aibread.AuthenticationError:
print ( "Invalid API key - check BREAD_API_KEY environment variable" )
except aibread.PermissionDeniedError:
print ( "Valid key but insufficient permissions" )
Not Found Errors
import aibread
from aibread import Bread
client = Bread()
try :
repo = client.repo.get( "my_repo" )
except aibread.NotFoundError:
# Create if doesn't exist
repo = client.repo.set( repo_name = "my_repo" )
print ( "Repository created" )
Conflict Errors
import aibread
from aibread import Bread
client = Bread()
try :
repo = client.repo.set(
repo_name = "existing_repo" ,
base_model = "different_model"
)
except aibread.ConflictError:
print ( "Cannot change base_model of existing repository" )
Rate Limiting
import aibread
import time
def retry_with_backoff ( fn , max_retries = 3 ):
"""Retry function with exponential backoff"""
for attempt in range (max_retries):
try :
return fn()
except aibread.RateLimitError:
if attempt == max_retries - 1 :
raise
wait_time = 2 ** attempt
print ( f "Rate limited, waiting { wait_time } s" )
time.sleep(wait_time)
# Usage
repos = retry_with_backoff( lambda : client.repo.list())
Connection Errors
import aibread
import time
def retry_on_connection_error ( fn , max_retries = 3 ):
"""Retry on connection failures"""
for attempt in range (max_retries):
try :
return fn()
except aibread.APIConnectionError as e:
if attempt == max_retries - 1 :
print ( f "Failed after { max_retries } attempts" )
raise
print ( f "Connection error, retry { attempt + 1 } / { max_retries } " )
time.sleep( 2 )
except aibread.APITimeoutError as e:
if attempt == max_retries - 1 :
raise
print ( f "Timeout, retry { attempt + 1 } / { max_retries } " )
time.sleep( 2 )
# Usage
repos = retry_on_connection_error( lambda : client.repo.list())
Error Properties
All APIError subclasses have:
message: Error message
request: Original request object
body: Response body (if available)
APIStatusError additionally has:
response: Full response object
statusCode: HTTP status code
import aibread
try :
client.repo.get( "nonexistent" )
except aibread.APIStatusError as e:
print ( f "Status code: { e.status_code } " )
print ( f "Response headers: { e.response.headers } " )
print ( f "Response body: { e.body } " )
print ( f "Request URL: { e.request.url } " )
Comprehensive Error Handler
import aibread
import logging
import time
logger = logging.getLogger( __name__ )
def handle_bread_error ( e : Exception ) -> dict :
"""Central error handler for Bread SDK"""
if isinstance (e, aibread.AuthenticationError):
logger.error( "Authentication failed" )
return { "error" : "auth_failed" , "retry" : False }
elif isinstance (e, aibread.PermissionDeniedError):
logger.error( "Permission denied" )
return { "error" : "permission_denied" , "retry" : False }
elif isinstance (e, aibread.NotFoundError):
logger.warning( "Resource not found" )
return { "error" : "not_found" , "retry" : False }
elif isinstance (e, aibread.ConflictError):
logger.warning( "Resource conflict" )
return { "error" : "conflict" , "retry" : False }
elif isinstance (e, aibread.RateLimitError):
logger.warning( "Rate limited" )
return { "error" : "rate_limited" , "retry" : True , "wait" : 60 }
elif isinstance (e, aibread.APITimeoutError):
logger.error( "Request timeout" )
return { "error" : "timeout" , "retry" : True }
elif isinstance (e, aibread.APIConnectionError):
logger.error( "Connection failed" )
return { "error" : "connection_failed" , "retry" : True }
elif isinstance (e, aibread.InternalServerError):
logger.error( "Server error" )
return { "error" : "server_error" , "retry" : True }
elif isinstance (e, aibread.APIStatusError):
logger.error( f "API error: { e.status_code } " )
return { "error" : "api_error" , "status" : e.status_code, "retry" : e.status_code >= 500 }
else :
logger.error( f "Unexpected error: { e } " )
return { "error" : "unknown" , "retry" : False }
# Usage
try :
result = client.repo.list()
except Exception as e:
error_info = handle_bread_error(e)
if error_info[ "retry" ]:
# Retry logic
time.sleep(error_info.get( "wait" , 5 ))
result = client.repo.list()
else :
# Handle non-retryable error
raise
Retry Configuration
The SDK automatically retries certain errors:
from aibread import Bread
# Default: 2 retries
client = Bread()
# Disable retries
client = Bread( max_retries = 0 )
# Custom retry count
client = Bread( max_retries = 5 )
# Per-request override
client.with_options( max_retries = 3 ).repo.list()
Auto-retried conditions :
408 Request Timeout
409 Conflict
429 Rate Limit
≥500 Internal Server Errors
Connection errors
Timeout Configuration
Configure timeouts to prevent hanging:
import httpx
from aibread import Bread
# Global timeout (20 seconds)
client = Bread( timeout = 20.0 )
# Granular timeout control
client = Bread(
timeout = httpx.Timeout(
60.0 , # Total
connect = 2.0 , # Connection
read = 5.0 , # Read
write = 10.0 # Write
)
)
# Per-request override
try :
result = client.with_options( timeout = 5.0 ).repo.list()
except aibread.APITimeoutError:
print ( "Request timed out after 5 seconds" )
Async Error Handling
Error handling works the same with async client:
import asyncio
import aibread
from aibread import AsyncBread
async def safe_api_call ():
try :
async with AsyncBread() as client:
repos = await client.repo.list()
return repos
except aibread.APIConnectionError:
print ( "Connection failed" )
except aibread.AuthenticationError:
print ( "Auth failed" )
except aibread.APIStatusError as e:
print ( f "API error: { e.status_code } " )
asyncio.run(safe_api_call())
Best Practices
Catch Specific Errors First
Handle specific error types before catching generic APIError
Retry on transient errors (rate limits, timeouts, server errors)
Log status codes, request details, and error messages for debugging
Authentication and permission errors are not transient - fix the root cause
For production, implement circuit breakers to prevent cascading failures
Configure timeouts based on your use case (quick requests vs. long-running jobs)
Next Steps
Production Patterns Production-ready workflow examples
Advanced Features Advanced SDK capabilities