OpenStack Object Storage API Client Library
Comprehensive error handling with detailed HTTP context, transaction IDs, and Swift-specific error information.
Main exception class for Swift client operations with comprehensive HTTP context and error details.
class ClientException(Exception):
def __init__(
self,
msg,
http_scheme='',
http_host='',
http_port='',
http_path='',
http_query='',
http_status=None,
http_reason='',
http_device='',
http_response_content='',
http_response_headers=None
):
"""
Swift client exception with HTTP context.
Parameters:
- msg: str, primary error message
- http_scheme: str, HTTP scheme (http/https)
- http_host: str, HTTP host
- http_port: str, HTTP port
- http_path: str, HTTP request path
- http_query: str, HTTP query string
- http_status: int, HTTP status code
- http_reason: str, HTTP reason phrase
- http_device: str, Swift device identifier
- http_response_content: str, HTTP response body
- http_response_headers: dict, HTTP response headers
Attributes:
- msg: Primary error message
- http_*: HTTP request/response context
- transaction_id: Swift transaction ID from response headers
"""
@classmethod
def from_response(cls, resp, msg=None, body=None):
"""
Create ClientException from HTTP response.
Parameters:
- resp: HTTP response object
- msg: str, optional custom message (defaults to status and reason)
- body: bytes, optional response body (defaults to response content)
Returns:
ClientException: exception with response context
"""# HTTP 401 Unauthorized
try:
conn = Connection(authurl='...', user='...', key='wrong_password')
conn.get_account()
except ClientException as e:
if e.http_status == 401:
print(f"Authentication failed: {e.msg}")
print(f"Check credentials and try again")
# e.transaction_id contains Swift transaction ID for debugging# HTTP 403 Forbidden
try:
conn.put_object('restricted_container', 'file.txt', 'content')
except ClientException as e:
if e.http_status == 403:
print(f"Access denied: {e.msg}")
print(f"Check account permissions for container")# HTTP 404 Not Found
try:
headers, content = conn.get_object('nonexistent', 'file.txt')
except ClientException as e:
if e.http_status == 404:
print(f"Object not found: {e.msg}")
print(f"Container: {e.http_path.split('/')[-2]}")
print(f"Object: {e.http_path.split('/')[-1]}")# HTTP 409 Conflict
try:
conn.delete_container('container_with_objects')
except ClientException as e:
if e.http_status == 409:
print(f"Cannot delete non-empty container: {e.msg}")
print("Delete all objects first, then retry container deletion")# HTTP 498 Rate Limited or HTTP 429 Too Many Requests
try:
# Rapid operations that might trigger rate limiting
for i in range(1000):
conn.put_object('container', f'object_{i}', f'content_{i}')
except ClientException as e:
if e.http_status in (498, 429):
print(f"Rate limited: {e.msg}")
print("Reduce request rate or increase retry backoff")# HTTP 5xx Server Errors
try:
conn.get_object('container', 'object')
except ClientException as e:
if 500 <= e.http_status < 600:
print(f"Server error: {e.http_status} {e.http_reason}")
print(f"Transaction ID: {e.transaction_id}")
print("Retry the operation or contact Swift administrator")from swiftclient import Connection, ClientException
import time
def robust_swift_operation(conn, container, obj, content, max_retries=3):
"""Perform Swift operation with comprehensive error handling."""
for attempt in range(max_retries + 1):
try:
etag = conn.put_object(container, obj, content)
print(f"Successfully uploaded {obj} with ETag {etag}")
return etag
except ClientException as e:
print(f"Attempt {attempt + 1} failed: {e}")
if e.http_status == 401:
# Authentication error - don't retry
print("Authentication failed - check credentials")
raise
elif e.http_status == 403:
# Permission error - don't retry
print("Access denied - check permissions")
raise
elif e.http_status == 404:
# Container doesn't exist - create it and retry
print(f"Container '{container}' not found, creating...")
try:
conn.put_container(container)
print(f"Created container '{container}'")
continue # Retry the upload
except ClientException as create_error:
print(f"Failed to create container: {create_error}")
raise
elif e.http_status == 409:
# Conflict - might be temporary, retry
if attempt < max_retries:
wait_time = 2 ** attempt # Exponential backoff
print(f"Conflict error, retrying in {wait_time}s...")
time.sleep(wait_time)
continue
else:
raise
elif e.http_status in (498, 429):
# Rate limiting - back off and retry
if attempt < max_retries:
wait_time = 10 * (2 ** attempt) # Longer backoff for rate limits
print(f"Rate limited, retrying in {wait_time}s...")
time.sleep(wait_time)
continue
else:
raise
elif e.http_status is None:
# Network error
if attempt < max_retries:
wait_time = 5 * (2 ** attempt)
print(f"Network error, retrying in {wait_time}s...")
time.sleep(wait_time)
continue
else:
print("Max retries exceeded for network errors")
raise
elif 500 <= e.http_status < 600:
# Server error - retry with backoff
if attempt < max_retries:
wait_time = 3 * (2 ** attempt)
print(f"Server error {e.http_status}, retrying in {wait_time}s...")
print(f"Transaction ID: {e.transaction_id}")
time.sleep(wait_time)
continue
else:
print(f"Max retries exceeded for server error {e.http_status}")
print(f"Transaction ID: {e.transaction_id}")
raise
else:
# Other HTTP error - don't retry
print(f"HTTP {e.http_status} {e.http_reason} - not retrying")
raise
except Exception as e:
# Non-HTTP error (programming error, etc.)
print(f"Unexpected error: {type(e).__name__}: {e}")
raise
# Should never reach here
raise RuntimeError("Maximum retries exceeded")
# Usage
conn = Connection(...)
try:
robust_swift_operation(conn, 'documents', 'important.txt', 'Important content')
except ClientException as e:
print(f"Final error: {e}")def analyze_swift_error(e):
"""Analyze ClientException and provide detailed context."""
print(f"Error: {e.msg}")
print(f"HTTP Status: {e.http_status} {e.http_reason}")
if e.transaction_id:
print(f"Transaction ID: {e.transaction_id}")
# Reconstruct URL
if e.http_scheme and e.http_host:
url = f"{e.http_scheme}://{e.http_host}"
if e.http_port:
url += f":{e.http_port}"
if e.http_path:
url += e.http_path
if e.http_query:
url += f"?{e.http_query}"
print(f"Request URL: {url}")
# Parse path for Swift components
if e.http_path:
path_parts = e.http_path.strip('/').split('/')
if len(path_parts) >= 2:
print(f"Swift Account: {path_parts[1]}")
if len(path_parts) >= 3:
print(f"Container: {path_parts[2]}")
if len(path_parts) >= 4:
print(f"Object: {'/'.join(path_parts[3:])}")
# Response content analysis
if e.http_response_content:
content = e.http_response_content
if len(content) > 200:
content = content[:200] + "... (truncated)"
print(f"Response content: {content}")
# Common error patterns
if e.http_status == 422:
print("Unprocessable Entity - check request format and parameters")
elif e.http_status == 413:
print("Request Entity Too Large - object exceeds size limits")
elif e.http_status == 412:
print("Precondition Failed - check If-Match, If-None-Match headers")
elif e.http_status == 416:
print("Range Not Satisfiable - check Range header format")
# Usage
try:
conn.get_object('container', 'object')
except ClientException as e:
analyze_swift_error(e)from swiftclient.service import SwiftService, SwiftError
def handle_bulk_errors(results_generator, operation_name):
"""Handle errors from SwiftService bulk operations."""
success_count = 0
error_count = 0
for result in results_generator:
if result['success']:
success_count += 1
print(f"✓ {result.get('object', result.get('container', 'item'))}")
else:
error_count += 1
error = result['error']
item = result.get('object', result.get('container', 'unknown'))
print(f"✗ {item}: {error}")
# Handle specific SwiftError types
if isinstance(error, SwiftError):
if error.exception:
underlying = error.exception
if isinstance(underlying, ClientException):
if underlying.http_status == 404:
print(f" → Item not found, skipping")
elif underlying.http_status in (401, 403):
print(f" → Access denied, check permissions")
elif underlying.http_status >= 500:
print(f" → Server error, transaction: {underlying.transaction_id}")
# Container context
if error.container:
print(f" → Container: {error.container}")
# Object context
if error.obj:
print(f" → Object: {error.obj}")
# Segment context (for large objects)
if error.segment:
print(f" → Segment: {error.segment}")
print(f"\n{operation_name} Summary:")
print(f" Success: {success_count}")
print(f" Errors: {error_count}")
return success_count, error_count
# Usage
with SwiftService(options=auth_options) as swift:
# Handle upload errors
upload_objects = ['file1.txt', 'file2.txt', 'missing_file.txt']
results = swift.upload('container', upload_objects)
handle_bulk_errors(results, "Upload")
# Handle download errors
download_objects = ['existing.txt', 'missing.txt']
results = swift.download('container', download_objects)
handle_bulk_errors(results, "Download")def create_resilient_connection(options, max_connection_attempts=3):
"""Create Swift connection with automatic retry on connection errors."""
for attempt in range(max_connection_attempts):
try:
conn = Connection(**options)
# Test connection
conn.head_account()
print("Connection established successfully")
return conn
except ClientException as e:
if e.http_status == 401:
print(f"Authentication failed: {e.msg}")
if 'token' in options:
print("Token may be expired, removing from options")
options = options.copy()
del options['token']
continue
else:
raise # Credential error, don't retry
elif e.http_status is None:
# Network error
if attempt < max_connection_attempts - 1:
wait_time = 5 * (2 ** attempt)
print(f"Connection failed, retrying in {wait_time}s...")
time.sleep(wait_time)
continue
else:
raise
else:
# Other HTTP error
raise
except Exception as e:
print(f"Unexpected connection error: {e}")
if attempt < max_connection_attempts - 1:
time.sleep(2)
continue
else:
raise
raise RuntimeError("Failed to establish connection after all attempts")
# Usage
options = {
'authurl': 'https://identity.example.com:5000/v3',
'user': 'myuser',
'key': 'mypassword',
'auth_version': '3',
'os_options': {'project_name': 'myproject'}
}
conn = create_resilient_connection(options)Install with Tessl CLI
npx tessl i tessl/pypi-python-swiftclient