Description
Conduct a zero-based review (ZBR) of the exceptions.py module before the 1.0.0 release to ensure the exception hierarchy, naming, and public API are correct and stable. Since 1.0.0 freezes the API, any breaking changes to exceptions must happen now.
Current Exception Hierarchy
ZAELimiterError (base)
├── RateLimitError
│ └── RateLimitExceeded # noqa: N818 (not suffixed with Error)
├── InfrastructureError
│ ├── RateLimiterUnavailable # noqa: N818 (not suffixed with Error)
│ ├── StackCreationError
│ │ └── StackAlreadyExistsError
│ └── InfrastructureNotFoundError
├── EntityError
│ ├── EntityNotFoundError
│ └── EntityExistsError
├── VersionError
│ ├── VersionMismatchError
│ └── IncompatibleSchemaError
└── ValidationError
├── InvalidIdentifierError
└── InvalidNameError
Review Areas
Each area below should be evaluated and a decision recorded (keep, rename, restructure, or remove):
-
Naming consistency: RateLimitExceeded and RateLimiterUnavailable both require # noqa: N818 because they don't end in Error. Decide whether to rename them (e.g., RateLimitExceededError, RateLimiterUnavailableError) or keep current names and document the convention.
-
Hierarchy placement of RateLimiterUnavailable: It inherits from InfrastructureError, not RateLimitError. This means except RateLimitError does NOT catch unavailability. Verify this is intentional and document the rationale.
-
StackCreationError scope: This exception is raised during both stack creation AND stack updates (used in stack_manager.py for update failures). Evaluate whether it should be renamed to StackOperationError or split into separate exceptions.
-
ValidationError name collision: Shares a name with pydantic.ValidationError and django.core.exceptions.ValidationError. Evaluate whether this causes real confusion or is acceptable since it lives in the zae_limiter namespace.
-
Backward-compat aliases (table_name): Both RateLimiterUnavailable and InfrastructureNotFoundError have self.table_name = self.stack_name. Decide whether to deprecate and remove these before 1.0.0 or keep them.
-
Serialization consistency: Only RateLimitExceeded has as_dict() and retry_after_header. Decide whether other user-facing exceptions need serialization support.
-
StackAlreadyExistsError as subclass of StackCreationError: Since it takes the same constructor args (including events it never uses), evaluate whether this inheritance is correct or if it should be a direct InfrastructureError subclass.
-
Missing exception types: Check whether any error paths in the codebase raise generic Exception, ValueError, TypeError, or RuntimeError that should have dedicated library exceptions.
Acceptance Criteria
Description
Conduct a zero-based review (ZBR) of the
exceptions.pymodule before the 1.0.0 release to ensure the exception hierarchy, naming, and public API are correct and stable. Since 1.0.0 freezes the API, any breaking changes to exceptions must happen now.Current Exception Hierarchy
Review Areas
Each area below should be evaluated and a decision recorded (keep, rename, restructure, or remove):
Naming consistency:
RateLimitExceededandRateLimiterUnavailableboth require# noqa: N818because they don't end inError. Decide whether to rename them (e.g.,RateLimitExceededError,RateLimiterUnavailableError) or keep current names and document the convention.Hierarchy placement of
RateLimiterUnavailable: It inherits fromInfrastructureError, notRateLimitError. This meansexcept RateLimitErrordoes NOT catch unavailability. Verify this is intentional and document the rationale.StackCreationErrorscope: This exception is raised during both stack creation AND stack updates (used instack_manager.pyfor update failures). Evaluate whether it should be renamed toStackOperationErroror split into separate exceptions.ValidationErrorname collision: Shares a name withpydantic.ValidationErroranddjango.core.exceptions.ValidationError. Evaluate whether this causes real confusion or is acceptable since it lives in thezae_limiternamespace.Backward-compat aliases (
table_name): BothRateLimiterUnavailableandInfrastructureNotFoundErrorhaveself.table_name = self.stack_name. Decide whether to deprecate and remove these before 1.0.0 or keep them.Serialization consistency: Only
RateLimitExceededhasas_dict()andretry_after_header. Decide whether other user-facing exceptions need serialization support.StackAlreadyExistsErroras subclass ofStackCreationError: Since it takes the same constructor args (includingeventsit never uses), evaluate whether this inheritance is correct or if it should be a directInfrastructureErrorsubclass.Missing exception types: Check whether any error paths in the codebase raise generic
Exception,ValueError,TypeError, orRuntimeErrorthat should have dedicated library exceptions.Acceptance Criteria
DeprecationWarningDeprecationWarningadded (if keeping for 1.0.0) or are removed (if breaking before 1.0.0)__init__.py__all__list matches the final set of public exceptionstests/unit/cover the exception hierarchy (isinstancechecks for each category)CLAUDE.mdexception list matches the actual module contents