Skip to content

Add apify.errors: domain-level error taxonomy over apify-client exceptions #988

@vdusek

Description

@vdusek

Introduce a machine-readable, domain-level error taxonomy in a new apify.errors module. This is a low-risk, additive change with high leverage for machine-driven (agent) use.

It builds on apify-client-python#423: the client already exposes typed, HTTP-status-based exceptions (ApifyApiError + UnauthorizedError, ForbiddenError, NotFoundError, RateLimitError, ServerError, …). This issue adds the next layer up — Actor-run-aware errors with a .retryable flag — and maps the client's exceptions into it at the SDK boundary.

Problem

The SDK itself defines zero custom exceptions. Failures surface as generic builtins (RuntimeError, ValueError) or as raw apify_client errors. An autonomous agent can't branch on those to decide retry vs. reformulate input vs. pick another tool vs. give up. That needs typed, coded, .retryable-tagged errors with Actor-run semantics (a 429 is transient; a FAILED run with a bad input is not).

Proposed design

ApifyError(Exception) with code: str and retryable: bool, plus typed subclasses:

class ApifyError(Exception):
    code: str
    retryable: bool

class ActorRunError(ApifyError):
    def __init__(self, run: Run): ...   # .run_id .status .exit_code .status_message

class InputValidationError(ApifyError, ValueError): ...   # still caught by `except ValueError`
class ChargeLimitExceededError(ApifyError): ...
class RateLimitError(ApifyError):     retryable = True
class AuthenticationError(ApifyError): ...
class ActorTimeoutError(ApifyError):  retryable = True

Export the names additively from apify/__init__.py.

Mapping apify_clientapify.errors

Translate the client's exceptions at the SDK boundary (the client's RateLimitError / UnauthorizedError names overlap, so the mapping must be explicit):

apify_client.errors apify.errors
UnauthorizedError (401), ForbiddenError (403) AuthenticationError
RateLimitError (429) RateLimitError (retryable=True)
ServerError (5xx) ApifyError (retryable=True)
InvalidRequestError (400) InputValidationError / ApifyError
other ApifyApiError ApifyError

ActorRunError, ChargeLimitExceededError, and ActorTimeoutError are derived from run status / exit code, not from HTTP errors.

Acceptance criteria

  • apify.errors module with ApifyError(code, retryable) and the subclasses above.
  • apify_client exceptions mapped to the appropriate typed error at the SDK boundary.
  • Names exported additively from apify/__init__.py.
  • No existing method changes what it raises; except ValueError still catches InputValidationError.

Risk: Medium. New exceptions appear only on new code paths (opt-in raise_on_failure); existing methods keep raising RuntimeError / ValueError.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request.t-toolingIssues with this label are in the ownership of the tooling team.

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions