0
# Exception Handling
1
2
Comprehensive error handling with detailed HTTP context, transaction IDs, and Swift-specific error information.
3
4
## Capabilities
5
6
### ClientException Class
7
8
Main exception class for Swift client operations with comprehensive HTTP context and error details.
9
10
```python { .api }
11
class ClientException(Exception):
12
def __init__(
13
self,
14
msg,
15
http_scheme='',
16
http_host='',
17
http_port='',
18
http_path='',
19
http_query='',
20
http_status=None,
21
http_reason='',
22
http_device='',
23
http_response_content='',
24
http_response_headers=None
25
):
26
"""
27
Swift client exception with HTTP context.
28
29
Parameters:
30
- msg: str, primary error message
31
- http_scheme: str, HTTP scheme (http/https)
32
- http_host: str, HTTP host
33
- http_port: str, HTTP port
34
- http_path: str, HTTP request path
35
- http_query: str, HTTP query string
36
- http_status: int, HTTP status code
37
- http_reason: str, HTTP reason phrase
38
- http_device: str, Swift device identifier
39
- http_response_content: str, HTTP response body
40
- http_response_headers: dict, HTTP response headers
41
42
Attributes:
43
- msg: Primary error message
44
- http_*: HTTP request/response context
45
- transaction_id: Swift transaction ID from response headers
46
"""
47
48
@classmethod
49
def from_response(cls, resp, msg=None, body=None):
50
"""
51
Create ClientException from HTTP response.
52
53
Parameters:
54
- resp: HTTP response object
55
- msg: str, optional custom message (defaults to status and reason)
56
- body: bytes, optional response body (defaults to response content)
57
58
Returns:
59
ClientException: exception with response context
60
"""
61
```
62
63
## Common Exception Scenarios
64
65
### Authentication Errors
66
67
```python
68
# HTTP 401 Unauthorized
69
try:
70
conn = Connection(authurl='...', user='...', key='wrong_password')
71
conn.get_account()
72
except ClientException as e:
73
if e.http_status == 401:
74
print(f"Authentication failed: {e.msg}")
75
print(f"Check credentials and try again")
76
# e.transaction_id contains Swift transaction ID for debugging
77
```
78
79
### Permission Errors
80
81
```python
82
# HTTP 403 Forbidden
83
try:
84
conn.put_object('restricted_container', 'file.txt', 'content')
85
except ClientException as e:
86
if e.http_status == 403:
87
print(f"Access denied: {e.msg}")
88
print(f"Check account permissions for container")
89
```
90
91
### Not Found Errors
92
93
```python
94
# HTTP 404 Not Found
95
try:
96
headers, content = conn.get_object('nonexistent', 'file.txt')
97
except ClientException as e:
98
if e.http_status == 404:
99
print(f"Object not found: {e.msg}")
100
print(f"Container: {e.http_path.split('/')[-2]}")
101
print(f"Object: {e.http_path.split('/')[-1]}")
102
```
103
104
### Conflict Errors
105
106
```python
107
# HTTP 409 Conflict
108
try:
109
conn.delete_container('container_with_objects')
110
except ClientException as e:
111
if e.http_status == 409:
112
print(f"Cannot delete non-empty container: {e.msg}")
113
print("Delete all objects first, then retry container deletion")
114
```
115
116
### Rate Limiting
117
118
```python
119
# HTTP 498 Rate Limited or HTTP 429 Too Many Requests
120
try:
121
# Rapid operations that might trigger rate limiting
122
for i in range(1000):
123
conn.put_object('container', f'object_{i}', f'content_{i}')
124
except ClientException as e:
125
if e.http_status in (498, 429):
126
print(f"Rate limited: {e.msg}")
127
print("Reduce request rate or increase retry backoff")
128
```
129
130
### Server Errors
131
132
```python
133
# HTTP 5xx Server Errors
134
try:
135
conn.get_object('container', 'object')
136
except ClientException as e:
137
if 500 <= e.http_status < 600:
138
print(f"Server error: {e.http_status} {e.http_reason}")
139
print(f"Transaction ID: {e.transaction_id}")
140
print("Retry the operation or contact Swift administrator")
141
```
142
143
## Usage Examples
144
145
### Comprehensive Error Handling
146
147
```python
148
from swiftclient import Connection, ClientException
149
import time
150
151
def robust_swift_operation(conn, container, obj, content, max_retries=3):
152
"""Perform Swift operation with comprehensive error handling."""
153
154
for attempt in range(max_retries + 1):
155
try:
156
etag = conn.put_object(container, obj, content)
157
print(f"Successfully uploaded {obj} with ETag {etag}")
158
return etag
159
160
except ClientException as e:
161
print(f"Attempt {attempt + 1} failed: {e}")
162
163
if e.http_status == 401:
164
# Authentication error - don't retry
165
print("Authentication failed - check credentials")
166
raise
167
168
elif e.http_status == 403:
169
# Permission error - don't retry
170
print("Access denied - check permissions")
171
raise
172
173
elif e.http_status == 404:
174
# Container doesn't exist - create it and retry
175
print(f"Container '{container}' not found, creating...")
176
try:
177
conn.put_container(container)
178
print(f"Created container '{container}'")
179
continue # Retry the upload
180
except ClientException as create_error:
181
print(f"Failed to create container: {create_error}")
182
raise
183
184
elif e.http_status == 409:
185
# Conflict - might be temporary, retry
186
if attempt < max_retries:
187
wait_time = 2 ** attempt # Exponential backoff
188
print(f"Conflict error, retrying in {wait_time}s...")
189
time.sleep(wait_time)
190
continue
191
else:
192
raise
193
194
elif e.http_status in (498, 429):
195
# Rate limiting - back off and retry
196
if attempt < max_retries:
197
wait_time = 10 * (2 ** attempt) # Longer backoff for rate limits
198
print(f"Rate limited, retrying in {wait_time}s...")
199
time.sleep(wait_time)
200
continue
201
else:
202
raise
203
204
elif e.http_status is None:
205
# Network error
206
if attempt < max_retries:
207
wait_time = 5 * (2 ** attempt)
208
print(f"Network error, retrying in {wait_time}s...")
209
time.sleep(wait_time)
210
continue
211
else:
212
print("Max retries exceeded for network errors")
213
raise
214
215
elif 500 <= e.http_status < 600:
216
# Server error - retry with backoff
217
if attempt < max_retries:
218
wait_time = 3 * (2 ** attempt)
219
print(f"Server error {e.http_status}, retrying in {wait_time}s...")
220
print(f"Transaction ID: {e.transaction_id}")
221
time.sleep(wait_time)
222
continue
223
else:
224
print(f"Max retries exceeded for server error {e.http_status}")
225
print(f"Transaction ID: {e.transaction_id}")
226
raise
227
228
else:
229
# Other HTTP error - don't retry
230
print(f"HTTP {e.http_status} {e.http_reason} - not retrying")
231
raise
232
233
except Exception as e:
234
# Non-HTTP error (programming error, etc.)
235
print(f"Unexpected error: {type(e).__name__}: {e}")
236
raise
237
238
# Should never reach here
239
raise RuntimeError("Maximum retries exceeded")
240
241
# Usage
242
conn = Connection(...)
243
try:
244
robust_swift_operation(conn, 'documents', 'important.txt', 'Important content')
245
except ClientException as e:
246
print(f"Final error: {e}")
247
```
248
249
### Error Context Analysis
250
251
```python
252
def analyze_swift_error(e):
253
"""Analyze ClientException and provide detailed context."""
254
255
print(f"Error: {e.msg}")
256
print(f"HTTP Status: {e.http_status} {e.http_reason}")
257
258
if e.transaction_id:
259
print(f"Transaction ID: {e.transaction_id}")
260
261
# Reconstruct URL
262
if e.http_scheme and e.http_host:
263
url = f"{e.http_scheme}://{e.http_host}"
264
if e.http_port:
265
url += f":{e.http_port}"
266
if e.http_path:
267
url += e.http_path
268
if e.http_query:
269
url += f"?{e.http_query}"
270
print(f"Request URL: {url}")
271
272
# Parse path for Swift components
273
if e.http_path:
274
path_parts = e.http_path.strip('/').split('/')
275
if len(path_parts) >= 2:
276
print(f"Swift Account: {path_parts[1]}")
277
if len(path_parts) >= 3:
278
print(f"Container: {path_parts[2]}")
279
if len(path_parts) >= 4:
280
print(f"Object: {'/'.join(path_parts[3:])}")
281
282
# Response content analysis
283
if e.http_response_content:
284
content = e.http_response_content
285
if len(content) > 200:
286
content = content[:200] + "... (truncated)"
287
print(f"Response content: {content}")
288
289
# Common error patterns
290
if e.http_status == 422:
291
print("Unprocessable Entity - check request format and parameters")
292
elif e.http_status == 413:
293
print("Request Entity Too Large - object exceeds size limits")
294
elif e.http_status == 412:
295
print("Precondition Failed - check If-Match, If-None-Match headers")
296
elif e.http_status == 416:
297
print("Range Not Satisfiable - check Range header format")
298
299
# Usage
300
try:
301
conn.get_object('container', 'object')
302
except ClientException as e:
303
analyze_swift_error(e)
304
```
305
306
### Bulk Operation Error Handling
307
308
```python
309
from swiftclient.service import SwiftService, SwiftError
310
311
def handle_bulk_errors(results_generator, operation_name):
312
"""Handle errors from SwiftService bulk operations."""
313
314
success_count = 0
315
error_count = 0
316
317
for result in results_generator:
318
if result['success']:
319
success_count += 1
320
print(f"✓ {result.get('object', result.get('container', 'item'))}")
321
else:
322
error_count += 1
323
error = result['error']
324
item = result.get('object', result.get('container', 'unknown'))
325
326
print(f"✗ {item}: {error}")
327
328
# Handle specific SwiftError types
329
if isinstance(error, SwiftError):
330
if error.exception:
331
underlying = error.exception
332
if isinstance(underlying, ClientException):
333
if underlying.http_status == 404:
334
print(f" → Item not found, skipping")
335
elif underlying.http_status in (401, 403):
336
print(f" → Access denied, check permissions")
337
elif underlying.http_status >= 500:
338
print(f" → Server error, transaction: {underlying.transaction_id}")
339
340
# Container context
341
if error.container:
342
print(f" → Container: {error.container}")
343
344
# Object context
345
if error.obj:
346
print(f" → Object: {error.obj}")
347
348
# Segment context (for large objects)
349
if error.segment:
350
print(f" → Segment: {error.segment}")
351
352
print(f"\n{operation_name} Summary:")
353
print(f" Success: {success_count}")
354
print(f" Errors: {error_count}")
355
356
return success_count, error_count
357
358
# Usage
359
with SwiftService(options=auth_options) as swift:
360
# Handle upload errors
361
upload_objects = ['file1.txt', 'file2.txt', 'missing_file.txt']
362
results = swift.upload('container', upload_objects)
363
handle_bulk_errors(results, "Upload")
364
365
# Handle download errors
366
download_objects = ['existing.txt', 'missing.txt']
367
results = swift.download('container', download_objects)
368
handle_bulk_errors(results, "Download")
369
```
370
371
### Connection Error Recovery
372
373
```python
374
def create_resilient_connection(options, max_connection_attempts=3):
375
"""Create Swift connection with automatic retry on connection errors."""
376
377
for attempt in range(max_connection_attempts):
378
try:
379
conn = Connection(**options)
380
# Test connection
381
conn.head_account()
382
print("Connection established successfully")
383
return conn
384
385
except ClientException as e:
386
if e.http_status == 401:
387
print(f"Authentication failed: {e.msg}")
388
if 'token' in options:
389
print("Token may be expired, removing from options")
390
options = options.copy()
391
del options['token']
392
continue
393
else:
394
raise # Credential error, don't retry
395
396
elif e.http_status is None:
397
# Network error
398
if attempt < max_connection_attempts - 1:
399
wait_time = 5 * (2 ** attempt)
400
print(f"Connection failed, retrying in {wait_time}s...")
401
time.sleep(wait_time)
402
continue
403
else:
404
raise
405
406
else:
407
# Other HTTP error
408
raise
409
410
except Exception as e:
411
print(f"Unexpected connection error: {e}")
412
if attempt < max_connection_attempts - 1:
413
time.sleep(2)
414
continue
415
else:
416
raise
417
418
raise RuntimeError("Failed to establish connection after all attempts")
419
420
# Usage
421
options = {
422
'authurl': 'https://identity.example.com:5000/v3',
423
'user': 'myuser',
424
'key': 'mypassword',
425
'auth_version': '3',
426
'os_options': {'project_name': 'myproject'}
427
}
428
429
conn = create_resilient_connection(options)
430
```