Exceptions¶
Exception types raised by zae-limiter.
Exception Hierarchy¶
ZAELimiterError (base)
├── RateLimitError
│ ├── RateLimitExceeded
│ └── RateLimiterUnavailable
├── EntityError
│ ├── EntityNotFoundError
│ └── EntityExistsError
├── InfrastructureError
│ ├── StackCreationError
│ ├── StackAlreadyExistsError
│ └── InfrastructureNotFoundError
└── VersionError
├── VersionMismatchError
└── IncompatibleSchemaError
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) |
RateLimiterUnavailable
¶
Bases: RateLimitError
Raised when DynamoDB is unavailable and failure_mode=FAIL_CLOSED.
This indicates a transient infrastructure issue, not a rate limit.
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) |
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¶
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.
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.
Exception Handling Examples¶
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(...):
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,
"burst": 100,
"available": -5,
"requested": 10,
"exceeded": True,
"retry_after_seconds": 45.2,
},
{
"entity_id": "user-123",
"resource": "api",
"limit_name": "tpm",
"capacity": 10000,
"burst": 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.