LogClient.stream() and KeyValueStoreClient.getRecord({ stream: true }) set responseType: 'stream'. When such a request fails, the ApifyApiError it produces carries nothing usable beyond statusCode. The message reads Unexpected error: [object Object], and the type, message and data the API returned in the error body are all missing.
Root cause
parseResponseData (src/interceptors.ts:94) bails for any response type other than arraybuffer, so a streamed error body is never read. ApifyApiError then receives an IncomingMessage as response.data, JSON.stringify throws on its circular references, and the constructor falls back to Unexpected error: ${dataString} (src/apify_api_error.ts:100).
One side effect: catchNotFoundOrThrow can never match a streamed 404, because it tests err.type against record-not-found and there is no type to test. LogClient.stream() therefore throws on a 404 even when the log is addressed by ID, and both the undefined arm of its return type and the if (!logStream) return guard in StreamedLog are unreachable.
Expected behavior
An error from a streaming request should carry the same type, message and data as one from a regular request. Buffering the error body when responseType is stream would get there.
Open questions
Parsing the body makes catchNotFoundOrThrow start matching, which flips LogClient.stream() back to resolving to undefined on a 404 for ID-addressed clients. #1042 documented and pinned the current throwing behavior, so either outcome is a deliberate choice that has to land across three places at once:
src/resource_clients/log.ts, both the stream() docstring and its Promise<Readable | undefined> return type.
docs/04_upgrading/upgrading_v3.md, the paragraph stating that stream() is not part of the v3 change.
test/logs.test.ts, the stream() throws on 404 status code case.
Found while reviewing #1042.
✍️ Drafted by Claude Code
LogClient.stream()andKeyValueStoreClient.getRecord({ stream: true })setresponseType: 'stream'. When such a request fails, theApifyApiErrorit produces carries nothing usable beyondstatusCode. The message readsUnexpected error: [object Object], and thetype,messageanddatathe API returned in the error body are all missing.Root cause
parseResponseData(src/interceptors.ts:94) bails for any response type other thanarraybuffer, so a streamed error body is never read.ApifyApiErrorthen receives anIncomingMessageasresponse.data,JSON.stringifythrows on its circular references, and the constructor falls back toUnexpected error: ${dataString}(src/apify_api_error.ts:100).One side effect:
catchNotFoundOrThrowcan never match a streamed 404, because it testserr.typeagainstrecord-not-foundand there is no type to test.LogClient.stream()therefore throws on a 404 even when the log is addressed by ID, and both theundefinedarm of its return type and theif (!logStream) returnguard inStreamedLogare unreachable.Expected behavior
An error from a streaming request should carry the same
type,messageanddataas one from a regular request. Buffering the error body whenresponseTypeisstreamwould get there.Open questions
Parsing the body makes
catchNotFoundOrThrowstart matching, which flipsLogClient.stream()back to resolving toundefinedon a 404 for ID-addressed clients. #1042 documented and pinned the current throwing behavior, so either outcome is a deliberate choice that has to land across three places at once:src/resource_clients/log.ts, both thestream()docstring and itsPromise<Readable | undefined>return type.docs/04_upgrading/upgrading_v3.md, the paragraph stating thatstream()is not part of the v3 change.test/logs.test.ts, thestream() throws on 404 status codecase.Found while reviewing #1042.
✍️ Drafted by Claude Code