Edited 2026-08-18. This issue originally reported ERR_STREAM_PREMATURE_CLOSE as a node-fetch chunked-transfer bug. That diagnosis was wrong: the root cause was in Node core and is already fixed. I've rewritten the issue to cover what actually remains. Original context is summarised under "Background" below.
Request
Two small things I ran into while working around a transport problem in a production service.
1. makeFetchRequest is protected, but urlBase is private
makeFetchRequest (index.ts:292) is protected, so subclasses are clearly intended to be able to override the HTTP transport. But urlBase (index.ts:203) is private, so a subclass cannot read the base URL that method needs.
The result is that any override has to rebuild the URL itself and hardcode the hosts:
class NativeFetchAppStoreApiClient extends AppStoreServerAPIClient {
private readonly nativeUrlBase: string; // has to duplicate PRODUCTION_URL / SANDBOX_URL
protected async makeFetchRequest(path, parsedQueryParameters, method, requestBody, headers) {
const url = `${this.nativeUrlBase}${path}?${parsedQueryParameters.toString()}`;
...
}
}
If those hostnames ever change, subclasses silently keep calling the old ones.
Making urlBase protected would be a one-line change and would make the existing extension point usable as designed.
This overlaps with #352, which asks for the same seam to attach an outbound proxy agent. Two independent use cases for controlling the transport.
2. API requests have no timeout
makeFetchRequest issues requests with no timeout, and neither node-fetch nor built-in fetch applies a default. A connection that is accepted but never answered will hang indefinitely rather than failing.
This also looks inconsistent within the project:
jws_verification.ts:319 sets timeout: 30000 for the OCSP request
- the Python library's
_execute_request passes timeout=30 on every request
so the API client looks like an oversight rather than a deliberate choice.
Background
For context on how I hit this — the original report, kept short:
A production service saw getAllSubscriptionStatuses fail repeatedly with ERR_STREAM_PREMATURE_CLOSE while getTransactionInfo succeeded on the same client. We worked around it by subclassing and overriding makeFetchRequest to use Node's built-in fetch, which is where I ran into both points above.
The root cause turned out to be in Node, not in this library or in node-fetch's chunked handling. Node 24.17.0 added a response-queue-poisoning guard that attached a public 'data' listener to idle http.Agent sockets. node-fetch v2 tests socket.listenerCount('data') > 0 to decide whether a response was truncated, counted that guard listener, and reported complete responses as failures. Response size was irrelevant, which is why a status-filtered smaller response still failed for us.
Fixed in Node by nodejs/node#64004, released in 22.23.1, 24.18.0 (both 2026-06-23) and 26.4.0 (2026-06-24). nodejs/node#63989 is closed. I could not reproduce the failure on any current Node release, so I'm not filing this as a bug against this library and I'm not asking for the node-fetch dependency to be changed on account of it.
Happy to open a PR for either point above.
Request
Two small things I ran into while working around a transport problem in a production service.
1.
makeFetchRequestisprotected, buturlBaseisprivatemakeFetchRequest(index.ts:292) isprotected, so subclasses are clearly intended to be able to override the HTTP transport. ButurlBase(index.ts:203) isprivate, so a subclass cannot read the base URL that method needs.The result is that any override has to rebuild the URL itself and hardcode the hosts:
If those hostnames ever change, subclasses silently keep calling the old ones.
Making
urlBaseprotectedwould be a one-line change and would make the existing extension point usable as designed.This overlaps with #352, which asks for the same seam to attach an outbound proxy agent. Two independent use cases for controlling the transport.
2. API requests have no timeout
makeFetchRequestissues requests with no timeout, and neither node-fetch nor built-in fetch applies a default. A connection that is accepted but never answered will hang indefinitely rather than failing.This also looks inconsistent within the project:
jws_verification.ts:319setstimeout: 30000for the OCSP request_execute_requestpassestimeout=30on every requestso the API client looks like an oversight rather than a deliberate choice.
Background
For context on how I hit this — the original report, kept short:
A production service saw
getAllSubscriptionStatusesfail repeatedly withERR_STREAM_PREMATURE_CLOSEwhilegetTransactionInfosucceeded on the same client. We worked around it by subclassing and overridingmakeFetchRequestto use Node's built-in fetch, which is where I ran into both points above.The root cause turned out to be in Node, not in this library or in node-fetch's chunked handling. Node 24.17.0 added a response-queue-poisoning guard that attached a public
'data'listener to idlehttp.Agentsockets. node-fetch v2 testssocket.listenerCount('data') > 0to decide whether a response was truncated, counted that guard listener, and reported complete responses as failures. Response size was irrelevant, which is why a status-filtered smaller response still failed for us.Fixed in Node by nodejs/node#64004, released in 22.23.1, 24.18.0 (both 2026-06-23) and 26.4.0 (2026-06-24). nodejs/node#63989 is closed. I could not reproduce the failure on any current Node release, so I'm not filing this as a bug against this library and I'm not asking for the node-fetch dependency to be changed on account of it.
Happy to open a PR for either point above.