SIGN IN SIGN UP

fix: raise NotFoundError on ambiguous 404 responses (#755)

Follow-up to
https://github.com/apify/apify-client-python/pull/737#discussion_r3117539782

`.get()` previously collapsed every 404 into `None` via
`catch_not_found_or_throw`. That works for direct, ID-identified fetches
(`client.dataset(id).get()`), where a 404 unambiguously means the named
resource is missing. It's misleading whenever a 404 is ambiguous — the
client can't tell which resource in the path is actually gone, and
silently returning `None` hides the cause.

This PR identifies three categories where a 404 is ambiguous and
propagates `NotFoundError` instead:

1. **Chained calls without an ID** (`run.dataset()`,
`run.key_value_store()`, `run.request_queue()`, `run.log()`) — a 404
could mean either the parent run is missing or the default sub-resource.
Covered by the base `_get` / `_delete` via a `resource_id is None →
raise` guard, so `.delete()` on a chained client also raises now; direct
`dataset(id).delete()` keeps its idempotent-DELETE semantics.
2. **Chained `LogClient`** — `run.log().get()` / `.get_as_bytes()` /
`.stream()` raise; direct `client.log(id).get()` still returns `None`.
3. **Singleton sub-path endpoints** — `ScheduleClient.get_log`,
`TaskClient.get_input`, `DatasetClient.get_statistics`,
`UserClient.monthly_usage`, `UserClient.limits`, `WebhookClient.test`.
These hit a fixed path (`/.../{id}/log`, `/.../{id}/input`, etc.) where
a 404 effectively always means the parent is missing. Return types moved
from `T | None` to `T`.

Record-by-key lookups (`KeyValueStoreClient.get_record(key)`,
`RequestQueueClient.get_request(request_id)`) keep the existing "None on
missing" behavior — the 404 is specifically about the record/request,
which is the natural meaning.

A shared helper `catch_not_found_for_resource_or_throw(exc,
resource_id)` in `_utils.py` centralizes the `resource_id is None →
raise` pattern across all 10 call sites. The v3 upgrade guide documents
the new semantics. Sync/async tests cover every code path, including
`client.actor('id').last_run().dataset().get()` (happy path +
ambiguous-404 case).
V
Vlada Dusek committed
701185e6e8a98f0b14d83cab10139f1e19be3f47
Parent: 0995ca0
Committed by GitHub <noreply@github.com> on 4/23/2026, 11:01:05 AM