fix!: throw on ambiguous 404s instead of resolving to undefined - #1042
Merged
Conversation
Contributor
|
See more at https://github.com/apify/apify-client-js/actions/runs/34445566589#summary-102769495031 |
A 404 collapses into `undefined` only where it names one resource: a direct
`get()` or `delete()` on a client created with an ID, and the record lookups
`getRecord()` and `getRequest()`. Where it could mean either the parent or
the sub-resource is missing, the `ApifyApiError` now propagates.
- Chained clients without an ID (`run.dataset()`, `run.keyValueStore()`,
`run.requestQueue()`, `run.log()`, `build.log()`): `get()`, `delete()`,
`log().get()` and `log().stream()` throw. `client.log(id)` still resolves
to `undefined`.
- Singleton sub-path endpoints: `DatasetClient.getStatistics()`,
`UserClient.monthlyUsage()`, `UserClient.limits()`,
`ScheduleClient.getLog()`, `TaskClient.getInput()` and
`WebhookClient.test()` throw, and their return types drop `| undefined`.
- `StreamedLog` logs a warning and stops when the run log answers 404,
instead of leaving its background task rejected.
The mock server's text handler now sends the status code it derives from
the resource ID, so `run('404').log().get()` can be exercised.
Mirrors apify/apify-client-python#755. Closes #1030.
BREAKING CHANGE: `run.dataset()`, `run.keyValueStore()`,
`run.requestQueue()`, `run.log()` and `build.log()` throw an `ApifyApiError`
on a 404 instead of resolving to `undefined`. `DatasetClient.getStatistics()`,
`UserClient.monthlyUsage()`, `UserClient.limits()`, `ScheduleClient.getLog()`,
`TaskClient.getInput()` and `WebhookClient.test()` throw on a 404 and no
longer include `undefined` in their return types.
vdusek
force-pushed
the
fix/ambiguous-404-throws
branch
from
September 8, 2026 10:24
a7dff98 to
0225c15
Compare
janbuchar
approved these changes
Sep 9, 2026
Conflicts were in the error-handling guide, where both sides added a section, and in an integration assertion that v3 tightened. The merge also changes `LogClient.stream()`. On v3 `catchNotFoundOrThrow()` is a plain `instanceof NotFoundError` check, so a streamed 404 now matches and an ID-addressed log resolves to `undefined` like `get()` does. The docstring, the upgrade guide and the unit test follow that.
Both conflicts were import lists that each side extended, resolved as the union.
This was referenced Sep 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A 404 collapsed into
undefinedeverywhere, including where it can't be pinned to one resource:run.dataset().get()couldn't tell a missing run from a missing dataset. Those calls now throw.run.dataset(),run.keyValueStore(),run.requestQueue(),run.log(),build.log()) throw fromget(),delete(),log().get()andlog().stream(). A newcatchNotFoundForResourceOrThrow(err, this.id)keys on the ID, so ID-addressed clients still resolve toundefined.| undefined:getStatistics(),monthlyUsage(),limits(),getLog(),getInput(),test().getRecord(),getRequest(),recordExists()andlastRun().version(),build()andenvVar()reject an empty string, which used to address the collection.UserClient.get()is nowPromise<User | undefined>, matching what it always returned.getStreamedLog()on a missing run warns and stops.Merging
v3also movedLogClient.stream()under the same rule. #1041 madecatchNotFoundOrThrow()a plaininstanceof NotFoundErrorcheck, so a streamed 404 matches it andclient.log(id).stream()resolves toundefinedthe wayget()does. #1043 raised that flip as an open question; the unusable error body it reports is still open.Mirrors apify/apify-client-python#755.
Closes #1030
✍️ Drafted by Claude Code