Microsoft Azure Data Tables Client Library for Python
90
Comprehensive exception handling with specific error codes, transaction failure management, and detailed error information for robust Azure Tables application development.
Azure Tables library provides specific exception types for different error scenarios with detailed error information.
class TableTransactionError(HttpResponseError):
"""
Exception raised when a batch transaction operation fails.
Inherits from HttpResponseError and contains detailed information
about which operation failed and the specific error that occurred.
"""
def __init__(self, **kwargs: Any) -> None:
"""
Initialize transaction error.
Parameters:
- **kwargs: Keyword arguments passed to HttpResponseError base class
including message, response, and other error details
"""
@property
def index(self) -> int:
"""
Index of the failed operation in the transaction (0-based).
Automatically extracted from error message or defaults to 0
if index cannot be determined.
"""
@property
def error_code(self) -> TableErrorCode:
"""Specific error code for the failure."""
@property
def message(self) -> str:
"""Human-readable error message."""
@property
def additional_info(self) -> Mapping[str, Any]:
"""Additional error information and context."""
class RequestTooLargeError(TableTransactionError):
"""
Exception for HTTP 413 - Request Entity Too Large errors.
Raised when the request payload exceeds service limits.
Inherits all properties and methods from TableTransactionError.
"""from azure.data.tables import TableClient, TableTransactionError, RequestTooLargeError
table_client = TableClient.from_connection_string(conn_str, "orders")
# Handle transaction errors
operations = [
("create", {"PartitionKey": "batch", "RowKey": "001", "Name": "Order 1"}),
("create", {"PartitionKey": "batch", "RowKey": "002", "Name": "Order 2"}),
("update", {"PartitionKey": "batch", "RowKey": "999", "Name": "Non-existent"})
]
try:
results = table_client.submit_transaction(operations)
except TableTransactionError as e:
print(f"Transaction failed at operation {e.index}")
print(f"Error code: {e.error_code}")
print(f"Error message: {e.message}")
if e.additional_info:
print(f"Additional details: {e.additional_info}")
# Handle specific error
if e.error_code == TableErrorCode.RESOURCE_NOT_FOUND:
print("The entity being updated doesn't exist")
# Handle request size errors
try:
large_entity = {"PartitionKey": "big", "RowKey": "001"}
# Add lots of data that exceeds 1MB limit
large_entity["BigData"] = "x" * (1024 * 1024 + 1)
table_client.create_entity(large_entity)
except RequestTooLargeError as e:
print(f"Entity too large: {e.message}")
# Split data or reduce entity sizeComprehensive enumeration of error codes for different failure scenarios.
class TableErrorCode(str, Enum):
"""
Comprehensive error codes for Azure Tables service operations.
Covers authentication, authorization, resource management,
entity operations, validation, condition checking, and service errors.
"""
# Account and Authentication Errors
ACCOUNT_ALREADY_EXISTS = "AccountAlreadyExists"
ACCOUNT_BEING_CREATED = "AccountBeingCreated"
ACCOUNT_IS_DISABLED = "AccountIsDisabled"
AUTHENTICATION_FAILED = "AuthenticationFailed"
AUTHORIZATION_FAILURE = "AuthorizationFailure"
NO_AUTHENTICATION_INFORMATION = "NoAuthenticationInformation"
INVALID_AUTHENTICATION_INFO = "InvalidAuthenticationInfo"
INSUFFICIENT_ACCOUNT_PERMISSIONS = "InsufficientAccountPermissions"
# Condition and Concurrency Errors
CONDITION_HEADERS_NOT_SUPPORTED = "ConditionHeadersNotSupported"
CONDITION_NOT_MET = "ConditionNotMet"
UPDATE_CONDITION_NOT_SATISFIED = "UpdateConditionNotSatisfied"
MULTIPLE_CONDITION_HEADERS_NOT_SUPPORTED = "MultipleConditionHeadersNotSupported"
# Resource Management Errors
RESOURCE_NOT_FOUND = "ResourceNotFound"
RESOURCE_ALREADY_EXISTS = "ResourceAlreadyExists"
RESOURCE_TYPE_MISMATCH = "ResourceTypeMismatch"
TABLE_NOT_FOUND = "TableNotFound"
TABLE_ALREADY_EXISTS = "TableAlreadyExists"
TABLE_BEING_DELETED = "TableBeingDeleted"
# Entity Operation Errors
ENTITY_NOT_FOUND = "EntityNotFound"
ENTITY_ALREADY_EXISTS = "EntityAlreadyExists"
ENTITY_TOO_LARGE = "EntityTooLarge"
# Property and Data Validation Errors
DUPLICATE_PROPERTIES_SPECIFIED = "DuplicatePropertiesSpecified"
PROPERTIES_NEED_VALUE = "PropertiesNeedValue"
PROPERTY_NAME_INVALID = "PropertyNameInvalid"
PROPERTY_NAME_TOO_LONG = "PropertyNameTooLong"
PROPERTY_VALUE_TOO_LARGE = "PropertyValueTooLarge"
TOO_MANY_PROPERTIES = "TooManyProperties"
INVALID_VALUE_TYPE = "InvalidValueType"
INVALID_DUPLICATE_ROW = "InvalidDuplicateRow"
# Request Validation Errors
INVALID_INPUT = "InvalidInput"
INVALID_RESOURCE_NAME = "InvalidResourceName"
INVALID_HEADER_VALUE = "InvalidHeaderValue"
INVALID_HTTP_VERB = "InvalidHttpVerb"
INVALID_MD5 = "InvalidMd5"
INVALID_METADATA = "InvalidMetadata"
INVALID_QUERY_PARAMETER_VALUE = "InvalidQueryParameterValue"
INVALID_RANGE = "InvalidRange"
INVALID_URI = "InvalidUri"
INVALID_XML_DOCUMENT = "InvalidXmlDocument"
INVALID_XML_NODE_VALUE = "InvalidXmlNodeValue"
OUT_OF_RANGE_INPUT = "OutOfRangeInput"
OUT_OF_RANGE_QUERY_PARAMETER_VALUE = "OutOfRangeQueryParameterValue"
# Request Format and Size Errors
REQUEST_BODY_TOO_LARGE = "RequestBodyTooLarge"
REQUEST_URL_FAILED_TO_PARSE = "RequestUrlFailedToParse"
METADATA_TOO_LARGE = "MetadataTooLarge"
EMPTY_METADATA_KEY = "EmptyMetadataKey"
MD5_MISMATCH = "Md5Mismatch"
# Missing Required Elements
MISSING_CONTENT_LENGTH_HEADER = "MissingContentLengthHeader"
MISSING_REQUIRED_QUERY_PARAMETER = "MissingRequiredQueryParameter"
MISSING_REQUIRED_HEADER = "MissingRequiredHeader"
MISSING_REQUIRED_XML_NODE = "MissingRequiredXmlNode"
HOST_INFORMATION_NOT_PRESENT = "HostInformationNotPresent"
# Service and Operation Errors
INTERNAL_ERROR = "InternalError"
OPERATION_TIMED_OUT = "OperationTimedOut"
SERVER_BUSY = "ServerBusy"
METHOD_NOT_ALLOWED = "MethodNotAllowed"
NOT_IMPLEMENTED = "NotImplemented"
# Unsupported Operations
UNSUPPORTED_HEADER = "UnsupportedHeader"
UNSUPPORTED_XML_NODE = "UnsupportedXmlNode"
UNSUPPORTED_QUERY_PARAMETER = "UnsupportedQueryParameter"
UNSUPPORTED_HTTP_VERB = "UnsupportedHttpVerb"
JSON_FORMAT_NOT_SUPPORTED = "JsonFormatNotSupported"
# X-Method Related Errors
X_METHOD_INCORRECT_COUNT = "XMethodIncorrectCount"
X_METHOD_INCORRECT_VALUE = "XMethodIncorrectValue"
X_METHOD_NOT_USING_POST = "XMethodNotUsingPost"from azure.data.tables import TableClient, TableErrorCode
from azure.core.exceptions import ResourceNotFoundError, ResourceExistsError
table_client = TableClient.from_connection_string(conn_str, "products")
# Handle specific error scenarios
def safe_entity_operations():
try:
# Try to get entity
entity = table_client.get_entity("electronics", "laptop001")
return entity
except ResourceNotFoundError as e:
if hasattr(e, 'error_code') and e.error_code == TableErrorCode.ENTITY_NOT_FOUND:
print("Entity doesn't exist, creating new one")
new_entity = {
"PartitionKey": "electronics",
"RowKey": "laptop001",
"Name": "Default Laptop",
"Price": 999.99
}
return table_client.create_entity(new_entity)
else:
print(f"Table or other resource not found: {e}")
raise
except ResourceExistsError as e:
if hasattr(e, 'error_code') and e.error_code == TableErrorCode.ENTITY_ALREADY_EXISTS:
print("Entity already exists, updating instead")
# Handle duplicate creation attempt
return table_client.update_entity(entity)
else:
raise
# Check for authentication issues
def handle_auth_errors():
try:
tables = list(table_client.list_entities())
except Exception as e:
if hasattr(e, 'error_code'):
if e.error_code == TableErrorCode.AUTHENTICATION_FAILED:
print("Check your account key or connection string")
elif e.error_code == TableErrorCode.AUTHORIZATION_FAILURE:
print("Check your permissions or SAS token scope")
elif e.error_code == TableErrorCode.INSUFFICIENT_ACCOUNT_PERMISSIONS:
print("Account needs additional permissions for this operation")
raiseTypical error scenarios and recommended handling strategies.
from azure.data.tables import TableServiceClient
from azure.core.exceptions import ClientAuthenticationError
def handle_auth_scenarios():
"""Handle common authentication scenarios."""
try:
service_client = TableServiceClient.from_connection_string(
"DefaultEndpointsProtocol=https;AccountName=test;AccountKey=invalid"
)
tables = list(service_client.list_tables())
except ClientAuthenticationError as e:
print("Authentication failed:")
if "invalid account key" in str(e).lower():
print("- Check your account key in the connection string")
elif "account not found" in str(e).lower():
print("- Verify the account name is correct")
elif "signature did not match" in str(e).lower():
print("- Account key may be incorrect or rotated")
# Suggested recovery actions
print("Recovery suggestions:")
print("1. Verify connection string in Azure portal")
print("2. Check if account keys were recently rotated")
print("3. Ensure account name and key are correctly configured")from azure.data.tables import TableServiceClient
from azure.core.exceptions import ResourceNotFoundError, ResourceExistsError
def handle_resource_errors():
"""Handle table and resource management errors."""
service_client = TableServiceClient.from_connection_string(conn_str)
# Safe table creation
def create_table_safely(table_name: str):
try:
table_client = service_client.create_table(table_name)
print(f"Created table: {table_name}")
return table_client
except ResourceExistsError:
print(f"Table {table_name} already exists, getting existing client")
return service_client.get_table_client(table_name)
# Safe table deletion
def delete_table_safely(table_name: str):
try:
service_client.delete_table(table_name)
print(f"Deleted table: {table_name}")
except ResourceNotFoundError:
print(f"Table {table_name} doesn't exist, nothing to delete")
# Table operations with retry
def robust_table_operation(table_name: str):
max_retries = 3
for attempt in range(max_retries):
try:
table_client = service_client.get_table_client(table_name)
entities = list(table_client.list_entities())
return entities
except ResourceNotFoundError:
if attempt < max_retries - 1:
print(f"Table not found, creating... (attempt {attempt + 1})")
create_table_safely(table_name)
else:
raisefrom azure.data.tables import TableClient, UpdateMode
from azure.core.exceptions import ResourceNotFoundError, ResourceExistsError, ResourceModifiedError
def handle_entity_errors():
"""Handle entity-specific error scenarios."""
table_client = TableClient.from_connection_string(conn_str, "customers")
# Safe entity retrieval
def get_entity_safely(partition_key: str, row_key: str):
try:
return table_client.get_entity(partition_key, row_key)
except ResourceNotFoundError:
print(f"Entity ({partition_key}, {row_key}) not found")
return None
# Robust entity creation
def create_or_update_entity(entity_data):
try:
# Try creating first
result = table_client.create_entity(entity_data)
print("Entity created successfully")
return result
except ResourceExistsError:
print("Entity exists, updating instead")
try:
return table_client.update_entity(entity_data, mode=UpdateMode.REPLACE)
except ResourceNotFoundError:
# Race condition: entity was deleted between create failure and update
print("Entity disappeared, retrying create")
return table_client.create_entity(entity_data)
# Optimistic concurrency handling
def update_with_concurrency_check(partition_key: str, row_key: str, updates):
max_retries = 3
for attempt in range(max_retries):
try:
# Get current entity
entity = table_client.get_entity(partition_key, row_key)
# Apply updates
for key, value in updates.items():
entity[key] = value
# Update with concurrency check
return table_client.update_entity(
entity,
etag=entity.metadata['etag'],
match_condition=MatchConditions.IfNotModified
)
except ResourceModifiedError:
if attempt < max_retries - 1:
print(f"Concurrency conflict, retrying... (attempt {attempt + 1})")
continue
else:
print("Max retries exceeded, giving up")
raise
except ResourceNotFoundError:
print("Entity was deleted by another process")
raisefrom azure.data.tables import TableClient, TableTransactionError
def handle_batch_errors():
"""Handle batch transaction error scenarios."""
table_client = TableClient.from_connection_string(conn_str, "orders")
def robust_batch_operation(operations):
"""Execute batch with detailed error handling."""
try:
results = table_client.submit_transaction(operations)
print(f"Batch completed successfully: {len(results)} operations")
return results
except TableTransactionError as e:
print(f"Batch failed at operation {e.index}")
failed_operation = operations[e.index]
# Analyze the failed operation
operation_type, entity_data = failed_operation[:2]
print(f"Failed operation: {operation_type}")
print(f"Entity: PK={entity_data.get('PartitionKey')}, RK={entity_data.get('RowKey')}")
print(f"Error: {e.error_code} - {e.message}")
# Handle specific error types
if e.error_code == TableErrorCode.ENTITY_ALREADY_EXISTS:
print("Consider using upsert instead of create")
elif e.error_code == TableErrorCode.ENTITY_NOT_FOUND:
print("Entity doesn't exist for update/delete operation")
elif e.error_code == TableErrorCode.CONDITION_NOT_MET:
print("Concurrency condition failed, entity was modified")
# Option: Execute operations individually for partial success
return execute_operations_individually(operations, e.index)
def execute_operations_individually(operations, failed_index):
"""Execute operations one by one, skipping the failed one."""
results = []
for i, operation in enumerate(operations):
if i == failed_index:
print(f"Skipping failed operation {i}")
results.append(None)
continue
try:
operation_type, entity_data = operation[:2]
if operation_type == "create":
result = table_client.create_entity(entity_data)
elif operation_type == "update":
result = table_client.update_entity(entity_data)
elif operation_type == "upsert":
result = table_client.upsert_entity(entity_data)
elif operation_type == "delete":
table_client.delete_entity(entity_data)
result = {"status": "deleted"}
results.append(result)
print(f"Operation {i} completed individually")
except Exception as individual_error:
print(f"Operation {i} also failed individually: {individual_error}")
results.append(None)
return resultsCommon patterns for recovering from various error conditions.
import time
import random
from azure.core.exceptions import ServiceRequestError
def retry_with_backoff(func, max_retries=3, base_delay=1.0):
"""Execute function with exponential backoff retry."""
for attempt in range(max_retries):
try:
return func()
except ServiceRequestError as e:
if attempt == max_retries - 1:
raise # Re-raise on final attempt
# Calculate delay with jitter
delay = base_delay * (2 ** attempt) + random.uniform(0, 1)
print(f"Attempt {attempt + 1} failed, retrying in {delay:.2f}s")
time.sleep(delay)
# Usage
def unreliable_operation():
return table_client.create_entity(entity_data)
result = retry_with_backoff(unreliable_operation)from datetime import datetime, timedelta
class CircuitBreaker:
"""Circuit breaker for failing operations."""
def __init__(self, failure_threshold=5, timeout=60):
self.failure_threshold = failure_threshold
self.timeout = timeout
self.failure_count = 0
self.last_failure_time = None
self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
def call(self, func):
if self.state == "OPEN":
if datetime.now() - self.last_failure_time > timedelta(seconds=self.timeout):
self.state = "HALF_OPEN"
else:
raise Exception("Circuit breaker is OPEN")
try:
result = func()
self.on_success()
return result
except Exception as e:
self.on_failure()
raise
def on_success(self):
self.failure_count = 0
self.state = "CLOSED"
def on_failure(self):
self.failure_count += 1
self.last_failure_time = datetime.now()
if self.failure_count >= self.failure_threshold:
self.state = "OPEN"
# Usage
circuit_breaker = CircuitBreaker()
def protected_operation():
return circuit_breaker.call(lambda: table_client.list_entities())Install with Tessl CLI
npx tessl i tessl/pypi-azure-data-tablesdocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10