Skip to content

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

RateLimitExceeded(statuses)

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)

retry_after_header property

retry_after_header

Value for HTTP Retry-After header (integer seconds).

as_dict

as_dict()

Serialize for JSON API responses.

Returns a dictionary suitable for returning in a 429 response body.

RateLimiterUnavailable

RateLimiterUnavailable(message, cause=None, *, stack_name=None, entity_id=None, resource=None)

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

EntityNotFoundError(entity_id)

Bases: EntityError

Raised when an entity is not found.

EntityExistsError

EntityExistsError(entity_id)

Bases: EntityError

Raised when trying to create an entity that already exists.

Infrastructure Exceptions

StackCreationError

StackCreationError(stack_name, reason, events=None)

Bases: InfrastructureError

Raised when CloudFormation stack creation fails.

StackAlreadyExistsError

StackAlreadyExistsError(stack_name, reason, events=None)

Bases: StackCreationError

Raised when stack already exists (informational).

InfrastructureNotFoundError

InfrastructureNotFoundError(stack_name)

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

IncompatibleSchemaError(client_version, schema_version, message, migration_guide_url=None)

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.