Skip to content

Latest commit

 

History

History
68 lines (45 loc) · 6.6 KB

File metadata and controls

68 lines (45 loc) · 6.6 KB
id error-handling
title Error handling
description The exceptions an Actor can raise and how to handle them

import HandleCallErrorsSource from '!!raw-loader!roa-loader!./code/13_handle_call_errors.py'; import RetryTimedOutSource from '!!raw-loader!roa-loader!./code/13_retry_timed_out.py'; import ApiLink from '@theme/ApiLink'; import RunnableCodeBlock from '@site/src/components/RunnableCodeBlock';

When you run an Actor, exceptions come from a few layers: the Apify API client for failed API requests, the Apify SDK for misuse and invalid input, and the libraries you build on, such as Crawlee.

Errors from the Apify API

Every SDK operation that talks to the Apify API can raise ApifyApiError. Such operations include Actor.start, Actor.call, Actor.abort, Actor.metamorph, Actor.add_webhook, charging, and all storage operations on datasets, key-value stores, and request queues. The SDK raises these client exceptions as-is, so you keep the HTTP status code, the error type, and the response data on the exception.

ApifyApiError dispatches to a subclass based on the HTTP status code:

  • UnauthorizedError (401) and ForbiddenError (403) for an unauthorized or forbidden request.
  • NotFoundError (404) when the Actor, run, or storage doesn't exist.
  • ConflictError (409) for a conflicting request.
  • RateLimitError (429) when the API rate limit is hit.
  • ServerError for any 5xx response.
  • InvalidRequestError (400) when the API rejects the request as malformed.

The client retries rate-limited and server errors on its own, so you only see RateLimitError or ServerError once those retries are exhausted. The apify.errors module re-exports the whole client error hierarchy, so you can import everything from one place:

from apify.errors import ApifyApiError, NotFoundError, RateLimitError

To handle any API failure in one place, catch ApifyApiError, then branch on the subclass or the HTTP status_code. To react to a specific failure, catch its subclass first:

{HandleCallErrorsSource}

Misuse and invalid input

The SDK raises standard Python exceptions when it's used incorrectly or given invalid input. These exceptions point to a bug or a bad argument in your code, so the fix is to correct the call rather than to catch the exception.

  • RuntimeError when an Actor method is used outside the async with Actor: block, either before initialization or after exit, or when the Actor is initialized twice.
  • ValueError for an invalid argument, such as a malformed timeout, an invalid proxy configuration, charging an automatically charged event by hand, or pushing data that is not JSON-serializable or is over the size limit.
  • TypeError for an argument of the wrong type.
  • ConnectionError when Actor.create_proxy_configuration verifies Apify Proxy access and the proxy reports that you have none.

Run failures

Actor.call and Actor.call_task wait for the run to finish and return it, whatever its final status. A finished run can be SUCCEEDED, FAILED, ABORTED, or TIMED-OUT, so check run.status before you rely on the run's output. A timed-out run is the one case where retrying can help, as long as you give it more time:

{RetryTimedOutSource}

The pay-per-event charge limit

Reaching the pay-per-event charge limit doesn't raise an error. Instead, the SDK caps charging and data pushing, while your Actor keeps running. When a single Actor.charge call crosses the limit, only the part that fits within the budget is billed, and charged_count on the returned ChargeResult reports how many events went through. Actor.push_data behaves the same way when given a charged_event_name. It writes only the items that fit within the budget.

To detect the limit, check the event_charge_limit_reached field on the ChargeResult. It's a return value and not an exception, so you can read it in a tight charging loop and stop your work once the budget runs out. For details, see Pay-per-event monetization.

Errors while crawling

If your Actor runs a Crawlee crawler, failures inside request handlers surface as Crawlee exceptions. Crawlee handles the retries and session rotation around them, so a single failing request doesn't stop the crawl. API calls you make from inside a handler still raise ApifyApiError. For how to handle those errors, see Errors from the Apify API.

Conclusion

Most failures you handle at runtime are ApifyApiError from the API client. Catch it to cover any API failure, and reach for a subclass or the HTTP status_code when you need finer control. The standard RuntimeError, ValueError, and TypeError signal a bug or bad input, so correct the call rather than catch them. After Actor.call, check run.status to react to a failed run, and let Crawlee handle the errors raised inside a crawler.