Exceptions¶
Exception types raised by zae-limiter.
Exception Hierarchy¶
ZAELimiterError (base)
├── RateLimitError
│ └── RateLimitExceeded
├── EntityError
│ ├── EntityNotFoundError
│ └── EntityExistsError
├── InfrastructureError
│ ├── RateLimiterUnavailable
│ ├── StackCreationError
│ │ └── StackAlreadyExistsError
│ ├── InfrastructureNotFoundError
│ └── NamespaceNotFoundError
├── VersionError
│ ├── VersionMismatchError
│ └── IncompatibleSchemaError
└── ValidationError
├── InvalidIdentifierError
└── InvalidNameError
Base Exception¶
ZAELimiterError
¶
Bases: Exception
Base exception for all zae-limiter errors.
All exceptions raised by this library inherit from this class, allowing callers to catch all library-specific errors with a single except clause.
Rate Limit Exceptions¶
RateLimitExceeded
¶
Bases: RateLimitError
Raised when one or more rate limits would be exceeded.
Provides full visibility into ALL limits that were checked, both passed and failed, to help callers understand the full picture.
Attributes:
| Name | Type | Description |
|---|---|---|
statuses |
Status of ALL limits checked (both passed and failed) |
|
violations |
Only the limits that were exceeded |
|
passed |
Only the limits that passed |
|
retry_after_seconds |
Time until ALL requested capacity is available |
|
primary_violation |
The violation with longest retry time (bottleneck) |
Entity Exceptions¶
EntityNotFoundError
¶
Bases: EntityError
Raised when an entity is not found.
EntityExistsError
¶
Bases: EntityError
Raised when trying to create an entity that already exists.
Infrastructure Exceptions¶
RateLimiterUnavailable
¶
Bases: InfrastructureError
Raised when DynamoDB is unavailable and on_unavailable=OnUnavailable.BLOCK.
This indicates a transient infrastructure issue, not a rate limit. When using OnUnavailable.BLOCK (the default), your application should be prepared to catch this exception and handle degraded mode gracefully.
Attributes:
| Name | Type | Description |
|---|---|---|
cause |
The underlying exception that caused the unavailability |
|
stack_name |
The stack/table that was being accessed |
|
entity_id |
The entity being rate limited (if applicable) |
|
resource |
The resource being rate limited (if applicable) |
StackCreationError
¶
Bases: InfrastructureError
Raised when CloudFormation stack creation fails.
StackAlreadyExistsError
¶
InfrastructureNotFoundError
¶
Bases: InfrastructureError
Raised when expected infrastructure doesn't exist.
This typically means the CloudFormation stack or DynamoDB table hasn't been deployed yet.
NamespaceNotFoundError
¶
Bases: InfrastructureError
Raised when a namespace is not found in the registry.
Version Exceptions¶
VersionMismatchError
¶
VersionMismatchError(client_version, schema_version, lambda_version, message, can_auto_update=False)
Bases: VersionError
Raised when client and infrastructure versions are incompatible.
This error indicates that the client library version doesn't match the deployed infrastructure and auto-update is disabled or failed.
IncompatibleSchemaError
¶
Bases: VersionError
Raised when schema version requires manual migration.
This indicates a major version difference that cannot be automatically reconciled.
Validation Exceptions¶
ValidationError
¶
Bases: ZAELimiterError
Base exception for input validation errors.
This includes errors raised when invalid input is provided to models, such as invalid entity IDs, limit names, or resource names.
Attributes:
| Name | Type | Description |
|---|---|---|
field |
The name of the field that failed validation |
|
value |
The invalid value (truncated if too long) |
|
reason |
Human-readable explanation of why validation failed |
InvalidIdentifierError
¶
Bases: ValidationError
Raised when an identifier (entity_id, parent_id) is invalid.
Identifiers must: - Not be empty - Not contain the '#' character (used as key delimiter) - Not exceed 256 characters - Start with alphanumeric character - Contain only: alphanumeric, underscore, hyphen, dot, colon, @
InvalidNameError
¶
Bases: ValidationError
Raised when a name (limit_name, resource) is invalid.
Names must: - Not be empty - Start with a letter - Contain only alphanumeric characters, underscores, hyphens, and dots - Not exceed 64 characters - Not contain the '#' character
Exception Handling Examples¶
Connection Errors¶
from zae_limiter import Repository, NamespaceNotFoundError
try:
repo = await Repository.open("tenant-alpha")
except NamespaceNotFoundError as e:
# Namespace not registered — register it first or check for typos
print(f"Namespace not found: {e.namespace_name}")
Basic Handling¶
from zae_limiter import RateLimitExceeded, RateLimiterUnavailable
try:
async with limiter.acquire(
entity_id="user-123",
resource="api",
limits=[Limit.per_minute("rpm", 100)],
consume={"rpm": 1},
):
await do_work()
except RateLimitExceeded as e:
# Handle rate limit exceeded
print(f"Rate limited. Retry after {e.retry_after_seconds}s")
except RateLimiterUnavailable as e:
# Handle service unavailable
print(f"Service unavailable: {e}")
HTTP API Response¶
from fastapi import HTTPException
from fastapi.responses import JSONResponse
@app.post("/api/endpoint")
async def endpoint():
try:
async with limiter.acquire(...):
return await process_request()
except RateLimitExceeded as e:
return JSONResponse(
status_code=429,
content=e.as_dict(),
headers={"Retry-After": e.retry_after_header},
)
except RateLimiterUnavailable:
raise HTTPException(status_code=503, detail="Service temporarily unavailable")
Detailed Error Information¶
try:
async with limiter.acquire(
entity_id="user-123",
resource="gpt-4",
limits=[Limit.per_minute("rpm", 1)],
consume={"rpm": 2}, # Exceeds capacity to trigger error
):
pass
except RateLimitExceeded as e:
# All limit statuses (both passed and failed)
for status in e.statuses:
print(f"Limit: {status.limit_name}")
print(f" Entity: {status.entity_id}")
print(f" Available: {status.available}")
print(f" Requested: {status.requested}")
print(f" Exceeded: {status.exceeded}")
print(f" Retry after: {status.retry_after_seconds}s")
# Only the violations
print(f"Violations: {len(e.violations)}")
for v in e.violations:
print(f" - {v.limit_name}: {v.available} available")
# Only the passed limits
print(f"Passed: {len(e.passed)}")
# Primary bottleneck (longest wait time)
print(f"Bottleneck: {e.primary_violation.limit_name}")
print(f"Total retry after: {e.retry_after_seconds}s")
as_dict() Output¶
The as_dict() method returns a dictionary suitable for API responses:
{
"error": "rate_limit_exceeded",
"message": "Rate limit exceeded for user-123/api: [rpm]. Retry after 45.2s",
"retry_after_seconds": 45.2,
"retry_after_ms": 45200,
"limits": [
{
"entity_id": "user-123",
"resource": "api",
"limit_name": "rpm",
"capacity": 100,
"available": -5,
"requested": 10,
"exceeded": True,
"retry_after_seconds": 45.2,
},
{
"entity_id": "user-123",
"resource": "api",
"limit_name": "tpm",
"capacity": 10000,
"available": 8500,
"requested": 500,
"exceeded": False,
"retry_after_seconds": 0.0,
},
],
}
Single limits array
All limits (both exceeded and passed) are returned in a single limits array.
Use the exceeded field to distinguish between violations and passed limits.