0
# Error Handling
1
2
Comprehensive exception handling with specific error codes, transaction failure management, and detailed error information for robust Azure Tables application development.
3
4
## Capabilities
5
6
### Exception Types
7
8
Azure Tables library provides specific exception types for different error scenarios with detailed error information.
9
10
```python { .api }
11
class TableTransactionError(HttpResponseError):
12
"""
13
Exception raised when a batch transaction operation fails.
14
15
Inherits from HttpResponseError and contains detailed information
16
about which operation failed and the specific error that occurred.
17
"""
18
19
def __init__(self, **kwargs: Any) -> None:
20
"""
21
Initialize transaction error.
22
23
Parameters:
24
- **kwargs: Keyword arguments passed to HttpResponseError base class
25
including message, response, and other error details
26
"""
27
28
@property
29
def index(self) -> int:
30
"""
31
Index of the failed operation in the transaction (0-based).
32
33
Automatically extracted from error message or defaults to 0
34
if index cannot be determined.
35
"""
36
37
@property
38
def error_code(self) -> TableErrorCode:
39
"""Specific error code for the failure."""
40
41
@property
42
def message(self) -> str:
43
"""Human-readable error message."""
44
45
@property
46
def additional_info(self) -> Mapping[str, Any]:
47
"""Additional error information and context."""
48
49
class RequestTooLargeError(TableTransactionError):
50
"""
51
Exception for HTTP 413 - Request Entity Too Large errors.
52
53
Raised when the request payload exceeds service limits.
54
Inherits all properties and methods from TableTransactionError.
55
"""
56
```
57
58
#### Usage Example
59
60
```python
61
from azure.data.tables import TableClient, TableTransactionError, RequestTooLargeError
62
63
table_client = TableClient.from_connection_string(conn_str, "orders")
64
65
# Handle transaction errors
66
operations = [
67
("create", {"PartitionKey": "batch", "RowKey": "001", "Name": "Order 1"}),
68
("create", {"PartitionKey": "batch", "RowKey": "002", "Name": "Order 2"}),
69
("update", {"PartitionKey": "batch", "RowKey": "999", "Name": "Non-existent"})
70
]
71
72
try:
73
results = table_client.submit_transaction(operations)
74
except TableTransactionError as e:
75
print(f"Transaction failed at operation {e.index}")
76
print(f"Error code: {e.error_code}")
77
print(f"Error message: {e.message}")
78
79
if e.additional_info:
80
print(f"Additional details: {e.additional_info}")
81
82
# Handle specific error
83
if e.error_code == TableErrorCode.RESOURCE_NOT_FOUND:
84
print("The entity being updated doesn't exist")
85
86
# Handle request size errors
87
try:
88
large_entity = {"PartitionKey": "big", "RowKey": "001"}
89
# Add lots of data that exceeds 1MB limit
90
large_entity["BigData"] = "x" * (1024 * 1024 + 1)
91
table_client.create_entity(large_entity)
92
except RequestTooLargeError as e:
93
print(f"Entity too large: {e.message}")
94
# Split data or reduce entity size
95
```
96
97
### Error Code System
98
99
Comprehensive enumeration of error codes for different failure scenarios.
100
101
```python { .api }
102
class TableErrorCode(str, Enum):
103
"""
104
Comprehensive error codes for Azure Tables service operations.
105
106
Covers authentication, authorization, resource management,
107
entity operations, validation, condition checking, and service errors.
108
"""
109
110
# Account and Authentication Errors
111
ACCOUNT_ALREADY_EXISTS = "AccountAlreadyExists"
112
ACCOUNT_BEING_CREATED = "AccountBeingCreated"
113
ACCOUNT_IS_DISABLED = "AccountIsDisabled"
114
AUTHENTICATION_FAILED = "AuthenticationFailed"
115
AUTHORIZATION_FAILURE = "AuthorizationFailure"
116
NO_AUTHENTICATION_INFORMATION = "NoAuthenticationInformation"
117
INVALID_AUTHENTICATION_INFO = "InvalidAuthenticationInfo"
118
INSUFFICIENT_ACCOUNT_PERMISSIONS = "InsufficientAccountPermissions"
119
120
# Condition and Concurrency Errors
121
CONDITION_HEADERS_NOT_SUPPORTED = "ConditionHeadersNotSupported"
122
CONDITION_NOT_MET = "ConditionNotMet"
123
UPDATE_CONDITION_NOT_SATISFIED = "UpdateConditionNotSatisfied"
124
MULTIPLE_CONDITION_HEADERS_NOT_SUPPORTED = "MultipleConditionHeadersNotSupported"
125
126
# Resource Management Errors
127
RESOURCE_NOT_FOUND = "ResourceNotFound"
128
RESOURCE_ALREADY_EXISTS = "ResourceAlreadyExists"
129
RESOURCE_TYPE_MISMATCH = "ResourceTypeMismatch"
130
TABLE_NOT_FOUND = "TableNotFound"
131
TABLE_ALREADY_EXISTS = "TableAlreadyExists"
132
TABLE_BEING_DELETED = "TableBeingDeleted"
133
134
# Entity Operation Errors
135
ENTITY_NOT_FOUND = "EntityNotFound"
136
ENTITY_ALREADY_EXISTS = "EntityAlreadyExists"
137
ENTITY_TOO_LARGE = "EntityTooLarge"
138
139
# Property and Data Validation Errors
140
DUPLICATE_PROPERTIES_SPECIFIED = "DuplicatePropertiesSpecified"
141
PROPERTIES_NEED_VALUE = "PropertiesNeedValue"
142
PROPERTY_NAME_INVALID = "PropertyNameInvalid"
143
PROPERTY_NAME_TOO_LONG = "PropertyNameTooLong"
144
PROPERTY_VALUE_TOO_LARGE = "PropertyValueTooLarge"
145
TOO_MANY_PROPERTIES = "TooManyProperties"
146
INVALID_VALUE_TYPE = "InvalidValueType"
147
INVALID_DUPLICATE_ROW = "InvalidDuplicateRow"
148
149
# Request Validation Errors
150
INVALID_INPUT = "InvalidInput"
151
INVALID_RESOURCE_NAME = "InvalidResourceName"
152
INVALID_HEADER_VALUE = "InvalidHeaderValue"
153
INVALID_HTTP_VERB = "InvalidHttpVerb"
154
INVALID_MD5 = "InvalidMd5"
155
INVALID_METADATA = "InvalidMetadata"
156
INVALID_QUERY_PARAMETER_VALUE = "InvalidQueryParameterValue"
157
INVALID_RANGE = "InvalidRange"
158
INVALID_URI = "InvalidUri"
159
INVALID_XML_DOCUMENT = "InvalidXmlDocument"
160
INVALID_XML_NODE_VALUE = "InvalidXmlNodeValue"
161
OUT_OF_RANGE_INPUT = "OutOfRangeInput"
162
OUT_OF_RANGE_QUERY_PARAMETER_VALUE = "OutOfRangeQueryParameterValue"
163
164
# Request Format and Size Errors
165
REQUEST_BODY_TOO_LARGE = "RequestBodyTooLarge"
166
REQUEST_URL_FAILED_TO_PARSE = "RequestUrlFailedToParse"
167
METADATA_TOO_LARGE = "MetadataTooLarge"
168
EMPTY_METADATA_KEY = "EmptyMetadataKey"
169
MD5_MISMATCH = "Md5Mismatch"
170
171
# Missing Required Elements
172
MISSING_CONTENT_LENGTH_HEADER = "MissingContentLengthHeader"
173
MISSING_REQUIRED_QUERY_PARAMETER = "MissingRequiredQueryParameter"
174
MISSING_REQUIRED_HEADER = "MissingRequiredHeader"
175
MISSING_REQUIRED_XML_NODE = "MissingRequiredXmlNode"
176
HOST_INFORMATION_NOT_PRESENT = "HostInformationNotPresent"
177
178
# Service and Operation Errors
179
INTERNAL_ERROR = "InternalError"
180
OPERATION_TIMED_OUT = "OperationTimedOut"
181
SERVER_BUSY = "ServerBusy"
182
METHOD_NOT_ALLOWED = "MethodNotAllowed"
183
NOT_IMPLEMENTED = "NotImplemented"
184
185
# Unsupported Operations
186
UNSUPPORTED_HEADER = "UnsupportedHeader"
187
UNSUPPORTED_XML_NODE = "UnsupportedXmlNode"
188
UNSUPPORTED_QUERY_PARAMETER = "UnsupportedQueryParameter"
189
UNSUPPORTED_HTTP_VERB = "UnsupportedHttpVerb"
190
JSON_FORMAT_NOT_SUPPORTED = "JsonFormatNotSupported"
191
192
# X-Method Related Errors
193
X_METHOD_INCORRECT_COUNT = "XMethodIncorrectCount"
194
X_METHOD_INCORRECT_VALUE = "XMethodIncorrectValue"
195
X_METHOD_NOT_USING_POST = "XMethodNotUsingPost"
196
```
197
198
#### Usage Example
199
200
```python
201
from azure.data.tables import TableClient, TableErrorCode
202
from azure.core.exceptions import ResourceNotFoundError, ResourceExistsError
203
204
table_client = TableClient.from_connection_string(conn_str, "products")
205
206
# Handle specific error scenarios
207
def safe_entity_operations():
208
try:
209
# Try to get entity
210
entity = table_client.get_entity("electronics", "laptop001")
211
return entity
212
213
except ResourceNotFoundError as e:
214
if hasattr(e, 'error_code') and e.error_code == TableErrorCode.ENTITY_NOT_FOUND:
215
print("Entity doesn't exist, creating new one")
216
new_entity = {
217
"PartitionKey": "electronics",
218
"RowKey": "laptop001",
219
"Name": "Default Laptop",
220
"Price": 999.99
221
}
222
return table_client.create_entity(new_entity)
223
else:
224
print(f"Table or other resource not found: {e}")
225
raise
226
227
except ResourceExistsError as e:
228
if hasattr(e, 'error_code') and e.error_code == TableErrorCode.ENTITY_ALREADY_EXISTS:
229
print("Entity already exists, updating instead")
230
# Handle duplicate creation attempt
231
return table_client.update_entity(entity)
232
else:
233
raise
234
235
# Check for authentication issues
236
def handle_auth_errors():
237
try:
238
tables = list(table_client.list_entities())
239
240
except Exception as e:
241
if hasattr(e, 'error_code'):
242
if e.error_code == TableErrorCode.AUTHENTICATION_FAILED:
243
print("Check your account key or connection string")
244
elif e.error_code == TableErrorCode.AUTHORIZATION_FAILURE:
245
print("Check your permissions or SAS token scope")
246
elif e.error_code == TableErrorCode.INSUFFICIENT_ACCOUNT_PERMISSIONS:
247
print("Account needs additional permissions for this operation")
248
raise
249
```
250
251
### Common Error Scenarios
252
253
Typical error scenarios and recommended handling strategies.
254
255
#### Authentication and Authorization Errors
256
257
```python
258
from azure.data.tables import TableServiceClient
259
from azure.core.exceptions import ClientAuthenticationError
260
261
def handle_auth_scenarios():
262
"""Handle common authentication scenarios."""
263
264
try:
265
service_client = TableServiceClient.from_connection_string(
266
"DefaultEndpointsProtocol=https;AccountName=test;AccountKey=invalid"
267
)
268
tables = list(service_client.list_tables())
269
270
except ClientAuthenticationError as e:
271
print("Authentication failed:")
272
273
if "invalid account key" in str(e).lower():
274
print("- Check your account key in the connection string")
275
elif "account not found" in str(e).lower():
276
print("- Verify the account name is correct")
277
elif "signature did not match" in str(e).lower():
278
print("- Account key may be incorrect or rotated")
279
280
# Suggested recovery actions
281
print("Recovery suggestions:")
282
print("1. Verify connection string in Azure portal")
283
print("2. Check if account keys were recently rotated")
284
print("3. Ensure account name and key are correctly configured")
285
```
286
287
#### Resource Management Errors
288
289
```python
290
from azure.data.tables import TableServiceClient
291
from azure.core.exceptions import ResourceNotFoundError, ResourceExistsError
292
293
def handle_resource_errors():
294
"""Handle table and resource management errors."""
295
296
service_client = TableServiceClient.from_connection_string(conn_str)
297
298
# Safe table creation
299
def create_table_safely(table_name: str):
300
try:
301
table_client = service_client.create_table(table_name)
302
print(f"Created table: {table_name}")
303
return table_client
304
305
except ResourceExistsError:
306
print(f"Table {table_name} already exists, getting existing client")
307
return service_client.get_table_client(table_name)
308
309
# Safe table deletion
310
def delete_table_safely(table_name: str):
311
try:
312
service_client.delete_table(table_name)
313
print(f"Deleted table: {table_name}")
314
315
except ResourceNotFoundError:
316
print(f"Table {table_name} doesn't exist, nothing to delete")
317
318
# Table operations with retry
319
def robust_table_operation(table_name: str):
320
max_retries = 3
321
for attempt in range(max_retries):
322
try:
323
table_client = service_client.get_table_client(table_name)
324
entities = list(table_client.list_entities())
325
return entities
326
327
except ResourceNotFoundError:
328
if attempt < max_retries - 1:
329
print(f"Table not found, creating... (attempt {attempt + 1})")
330
create_table_safely(table_name)
331
else:
332
raise
333
```
334
335
#### Entity Operation Errors
336
337
```python
338
from azure.data.tables import TableClient, UpdateMode
339
from azure.core.exceptions import ResourceNotFoundError, ResourceExistsError, ResourceModifiedError
340
341
def handle_entity_errors():
342
"""Handle entity-specific error scenarios."""
343
344
table_client = TableClient.from_connection_string(conn_str, "customers")
345
346
# Safe entity retrieval
347
def get_entity_safely(partition_key: str, row_key: str):
348
try:
349
return table_client.get_entity(partition_key, row_key)
350
except ResourceNotFoundError:
351
print(f"Entity ({partition_key}, {row_key}) not found")
352
return None
353
354
# Robust entity creation
355
def create_or_update_entity(entity_data):
356
try:
357
# Try creating first
358
result = table_client.create_entity(entity_data)
359
print("Entity created successfully")
360
return result
361
362
except ResourceExistsError:
363
print("Entity exists, updating instead")
364
try:
365
return table_client.update_entity(entity_data, mode=UpdateMode.REPLACE)
366
except ResourceNotFoundError:
367
# Race condition: entity was deleted between create failure and update
368
print("Entity disappeared, retrying create")
369
return table_client.create_entity(entity_data)
370
371
# Optimistic concurrency handling
372
def update_with_concurrency_check(partition_key: str, row_key: str, updates):
373
max_retries = 3
374
375
for attempt in range(max_retries):
376
try:
377
# Get current entity
378
entity = table_client.get_entity(partition_key, row_key)
379
380
# Apply updates
381
for key, value in updates.items():
382
entity[key] = value
383
384
# Update with concurrency check
385
return table_client.update_entity(
386
entity,
387
etag=entity.metadata['etag'],
388
match_condition=MatchConditions.IfNotModified
389
)
390
391
except ResourceModifiedError:
392
if attempt < max_retries - 1:
393
print(f"Concurrency conflict, retrying... (attempt {attempt + 1})")
394
continue
395
else:
396
print("Max retries exceeded, giving up")
397
raise
398
except ResourceNotFoundError:
399
print("Entity was deleted by another process")
400
raise
401
```
402
403
#### Batch Transaction Error Handling
404
405
```python
406
from azure.data.tables import TableClient, TableTransactionError
407
408
def handle_batch_errors():
409
"""Handle batch transaction error scenarios."""
410
411
table_client = TableClient.from_connection_string(conn_str, "orders")
412
413
def robust_batch_operation(operations):
414
"""Execute batch with detailed error handling."""
415
416
try:
417
results = table_client.submit_transaction(operations)
418
print(f"Batch completed successfully: {len(results)} operations")
419
return results
420
421
except TableTransactionError as e:
422
print(f"Batch failed at operation {e.index}")
423
failed_operation = operations[e.index]
424
425
# Analyze the failed operation
426
operation_type, entity_data = failed_operation[:2]
427
print(f"Failed operation: {operation_type}")
428
print(f"Entity: PK={entity_data.get('PartitionKey')}, RK={entity_data.get('RowKey')}")
429
print(f"Error: {e.error_code} - {e.message}")
430
431
# Handle specific error types
432
if e.error_code == TableErrorCode.ENTITY_ALREADY_EXISTS:
433
print("Consider using upsert instead of create")
434
435
elif e.error_code == TableErrorCode.ENTITY_NOT_FOUND:
436
print("Entity doesn't exist for update/delete operation")
437
438
elif e.error_code == TableErrorCode.CONDITION_NOT_MET:
439
print("Concurrency condition failed, entity was modified")
440
441
# Option: Execute operations individually for partial success
442
return execute_operations_individually(operations, e.index)
443
444
def execute_operations_individually(operations, failed_index):
445
"""Execute operations one by one, skipping the failed one."""
446
447
results = []
448
for i, operation in enumerate(operations):
449
if i == failed_index:
450
print(f"Skipping failed operation {i}")
451
results.append(None)
452
continue
453
454
try:
455
operation_type, entity_data = operation[:2]
456
457
if operation_type == "create":
458
result = table_client.create_entity(entity_data)
459
elif operation_type == "update":
460
result = table_client.update_entity(entity_data)
461
elif operation_type == "upsert":
462
result = table_client.upsert_entity(entity_data)
463
elif operation_type == "delete":
464
table_client.delete_entity(entity_data)
465
result = {"status": "deleted"}
466
467
results.append(result)
468
print(f"Operation {i} completed individually")
469
470
except Exception as individual_error:
471
print(f"Operation {i} also failed individually: {individual_error}")
472
results.append(None)
473
474
return results
475
```
476
477
### Error Recovery Patterns
478
479
Common patterns for recovering from various error conditions.
480
481
#### Retry with Exponential Backoff
482
483
```python
484
import time
485
import random
486
from azure.core.exceptions import ServiceRequestError
487
488
def retry_with_backoff(func, max_retries=3, base_delay=1.0):
489
"""Execute function with exponential backoff retry."""
490
491
for attempt in range(max_retries):
492
try:
493
return func()
494
495
except ServiceRequestError as e:
496
if attempt == max_retries - 1:
497
raise # Re-raise on final attempt
498
499
# Calculate delay with jitter
500
delay = base_delay * (2 ** attempt) + random.uniform(0, 1)
501
print(f"Attempt {attempt + 1} failed, retrying in {delay:.2f}s")
502
time.sleep(delay)
503
504
# Usage
505
def unreliable_operation():
506
return table_client.create_entity(entity_data)
507
508
result = retry_with_backoff(unreliable_operation)
509
```
510
511
#### Circuit Breaker Pattern
512
513
```python
514
from datetime import datetime, timedelta
515
516
class CircuitBreaker:
517
"""Circuit breaker for failing operations."""
518
519
def __init__(self, failure_threshold=5, timeout=60):
520
self.failure_threshold = failure_threshold
521
self.timeout = timeout
522
self.failure_count = 0
523
self.last_failure_time = None
524
self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
525
526
def call(self, func):
527
if self.state == "OPEN":
528
if datetime.now() - self.last_failure_time > timedelta(seconds=self.timeout):
529
self.state = "HALF_OPEN"
530
else:
531
raise Exception("Circuit breaker is OPEN")
532
533
try:
534
result = func()
535
self.on_success()
536
return result
537
except Exception as e:
538
self.on_failure()
539
raise
540
541
def on_success(self):
542
self.failure_count = 0
543
self.state = "CLOSED"
544
545
def on_failure(self):
546
self.failure_count += 1
547
self.last_failure_time = datetime.now()
548
549
if self.failure_count >= self.failure_threshold:
550
self.state = "OPEN"
551
552
# Usage
553
circuit_breaker = CircuitBreaker()
554
555
def protected_operation():
556
return circuit_breaker.call(lambda: table_client.list_entities())
557
```