Skip to content

Commit 67962f0

Browse files
committed
docs: cross-link exception types in the error-handling page
1 parent d12c0e0 commit 67962f0

4 files changed

Lines changed: 69 additions & 18 deletions

File tree

docs/02_concepts/13_exceptions.mdx

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,24 @@ When you run an Actor, exceptions come from a few layers: the Apify API client f
1313

1414
## Errors from the Apify API
1515

16-
Every SDK operation that talks to the Apify API can raise `ApifyApiError`. This includes <ApiLink to="class/Actor#start">`Actor.start`</ApiLink>, <ApiLink to="class/Actor#call">`Actor.call`</ApiLink>, `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.
16+
Every SDK operation that talks to the Apify API can raise <ApiLink to="class/ApifyApiError">`ApifyApiError`</ApiLink>. This includes <ApiLink to="class/Actor#start">`Actor.start`</ApiLink>, <ApiLink to="class/Actor#call">`Actor.call`</ApiLink>, <ApiLink to="class/Actor#abort">`Actor.abort`</ApiLink>, <ApiLink to="class/Actor#metamorph">`Actor.metamorph`</ApiLink>, <ApiLink to="class/Actor#add_webhook">`Actor.add_webhook`</ApiLink>, 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.
1717

18-
`ApifyApiError` dispatches to a subclass based on the HTTP status code:
18+
<ApiLink to="class/ApifyApiError">`ApifyApiError`</ApiLink> dispatches to a subclass based on the HTTP status code:
1919

20-
- `UnauthorizedError` (401) and `ForbiddenError` (403) for an unauthorized or forbidden request.
21-
- `NotFoundError` (404) when the Actor, run, or storage does not exist.
22-
- `ConflictError` (409) for a conflicting request.
23-
- `RateLimitError` (429) when the API rate limit is hit.
24-
- `ServerError` for any 5xx response.
25-
- `InvalidRequestError` (400) when the API rejects the request as malformed.
20+
- <ApiLink to="class/UnauthorizedError">`UnauthorizedError`</ApiLink> (401) and <ApiLink to="class/ForbiddenError">`ForbiddenError`</ApiLink> (403) for an unauthorized or forbidden request.
21+
- <ApiLink to="class/NotFoundError">`NotFoundError`</ApiLink> (404) when the Actor, run, or storage does not exist.
22+
- <ApiLink to="class/ConflictError">`ConflictError`</ApiLink> (409) for a conflicting request.
23+
- <ApiLink to="class/RateLimitError">`RateLimitError`</ApiLink> (429) when the API rate limit is hit.
24+
- <ApiLink to="class/ServerError">`ServerError`</ApiLink> for any 5xx response.
25+
- <ApiLink to="class/InvalidRequestError">`InvalidRequestError`</ApiLink> (400) when the API rejects the request as malformed.
2626

27-
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:
27+
The client retries rate-limited and server errors on its own, so you only see <ApiLink to="class/RateLimitError">`RateLimitError`</ApiLink> or <ApiLink to="class/ServerError">`ServerError`</ApiLink> once those retries are exhausted. The `apify.errors` module re-exports the whole client error hierarchy, so you can import everything from one place:
2828

2929
```python
3030
from apify.errors import ApifyApiError, NotFoundError, RateLimitError
3131
```
3232

33-
Catch `ApifyApiError` to handle any API failure in one place, then branch on the subclass or the HTTP `status_code`. To react to a specific failure, catch its subclass first:
33+
Catch <ApiLink to="class/ApifyApiError">`ApifyApiError`</ApiLink> to handle any API failure in one place, then branch on the subclass or the HTTP `status_code`. To react to a specific failure, catch its subclass first:
3434

3535
<RunnableCodeBlock className="language-python" language="python">
3636
{HandleCallErrorsSource}
@@ -40,10 +40,10 @@ Catch `ApifyApiError` to handle any API failure in one place, then branch on the
4040

4141
The SDK raises standard Python exceptions when it is used incorrectly or given invalid input. These 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.
4242

43-
- `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.
44-
- `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.
45-
- `TypeError` for an argument of the wrong type.
46-
- `ConnectionError` when <ApiLink to="class/Actor#create_proxy_configuration">`Actor.create_proxy_configuration`</ApiLink> verifies Apify Proxy access and the proxy reports that you have none.
43+
- [`RuntimeError`](https://docs.python.org/3/library/exceptions.html#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.
44+
- [`ValueError`](https://docs.python.org/3/library/exceptions.html#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.
45+
- [`TypeError`](https://docs.python.org/3/library/exceptions.html#TypeError) for an argument of the wrong type.
46+
- [`ConnectionError`](https://docs.python.org/3/library/exceptions.html#ConnectionError) when <ApiLink to="class/Actor#create_proxy_configuration">`Actor.create_proxy_configuration`</ApiLink> verifies Apify Proxy access and the proxy reports that you have none.
4747

4848
## Run failures
4949

@@ -59,4 +59,8 @@ If your Actor runs a [Crawlee](https://crawlee.dev/python) crawler, failures ins
5959

6060
## The pay-per-event charge limit
6161

62-
Reaching the pay-per-event charge limit does not raise an error. The SDK caps charging and data pushing instead, and your Actor keeps running. To detect the limit, check the `event_charge_limit_reached` field on the `ChargeResult` returned by <ApiLink to="class/Actor#charge">`Actor.charge`</ApiLink> or `Actor.push_data`. For details, see [Pay-per-event monetization](./pay-per-event).
62+
Reaching the pay-per-event charge limit does not raise an error. The SDK caps charging and data pushing instead, and your Actor keeps running. To detect the limit, check the `event_charge_limit_reached` field on the `ChargeResult` returned by <ApiLink to="class/Actor#charge">`Actor.charge`</ApiLink> or <ApiLink to="class/Actor#push_data">`Actor.push_data`</ApiLink>. For details, see [Pay-per-event monetization](./pay-per-event).
63+
64+
## Conclusion
65+
66+
Most failures you handle at runtime are <ApiLink to="class/ApifyApiError">`ApifyApiError`</ApiLink> 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`](https://docs.python.org/3/library/exceptions.html#RuntimeError), [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError), and [`TypeError`](https://docs.python.org/3/library/exceptions.html#TypeError) signal a bug or bad input, so correct the call rather than catch them. After <ApiLink to="class/Actor#call">`Actor.call`</ApiLink>, check `run.status` to react to a failed run, and let Crawlee handle the errors raised inside a crawler.

src/apify/_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def is_running_in_ipython() -> bool:
7474
'Actor',
7575
'Charging',
7676
'Configuration',
77+
'Errors',
7778
'Event data',
7879
'Event managers',
7980
'Events',

src/apify/errors.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
"""`apify.errors` re-exports the Apify API client's error hierarchy.
2+
3+
Callers get a single import location for every error raised by an operation that talks to the Apify API. The SDK
4+
raises these client exceptions as-is and does not wrap them in its own types. See
5+
https://docs.apify.com/api/client/python for the full client error reference.
6+
"""
7+
18
from __future__ import annotations
29

3-
# `apify.errors` re-exports the Apify API client's error hierarchy so callers have a single import location for every
4-
# error raised by an operation that talks to the Apify API. The SDK raises these client exceptions as-is and does not
5-
# wrap them in its own types. See https://docs.apify.com/api/client/python for the full client error reference.
610
from apify_client.errors import (
711
ApifyApiError,
812
ApifyClientError,

website/docusaurus.config.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const GROUP_ORDER = [
99
'Actor',
1010
'Charging',
1111
'Configuration',
12+
'Errors',
1213
'Event data',
1314
'Event managers',
1415
'Events',
@@ -149,6 +150,47 @@ module.exports = {
149150
moduleShortcutsPath: join(__dirname, '/module_shortcuts.json'),
150151
},
151152
reexports: [
153+
// Errors
154+
{
155+
url: 'https://docs.apify.com/api/client/python/reference/class/ApifyApiError',
156+
group: 'Errors',
157+
},
158+
{
159+
url: 'https://docs.apify.com/api/client/python/reference/class/ApifyClientError',
160+
group: 'Errors',
161+
},
162+
{
163+
url: 'https://docs.apify.com/api/client/python/reference/class/ConflictError',
164+
group: 'Errors',
165+
},
166+
{
167+
url: 'https://docs.apify.com/api/client/python/reference/class/ForbiddenError',
168+
group: 'Errors',
169+
},
170+
{
171+
url: 'https://docs.apify.com/api/client/python/reference/class/InvalidRequestError',
172+
group: 'Errors',
173+
},
174+
{
175+
url: 'https://docs.apify.com/api/client/python/reference/class/InvalidResponseBodyError',
176+
group: 'Errors',
177+
},
178+
{
179+
url: 'https://docs.apify.com/api/client/python/reference/class/NotFoundError',
180+
group: 'Errors',
181+
},
182+
{
183+
url: 'https://docs.apify.com/api/client/python/reference/class/RateLimitError',
184+
group: 'Errors',
185+
},
186+
{
187+
url: 'https://docs.apify.com/api/client/python/reference/class/ServerError',
188+
group: 'Errors',
189+
},
190+
{
191+
url: 'https://docs.apify.com/api/client/python/reference/class/UnauthorizedError',
192+
group: 'Errors',
193+
},
152194
// Storages
153195
{
154196
url: 'https://crawlee.dev/python/api/class/Storage',

0 commit comments

Comments
 (0)