From 899a81cdc4e5956c2a4eab3dd144b696e9b97582 Mon Sep 17 00:00:00 2001 From: spencerhunter Date: Mon, 22 Jun 2026 20:09:35 -0500 Subject: [PATCH 1/2] Disable node-fetch gzip handling to fix ERR_STREAM_PREMATURE_CLOSE Pass `compress: false` on all node-fetch requests (token exchange in Auth.js and the get/post/delete API calls in Token.js) so node-fetch skips the Gunzip pipeline that surfaces false-positive ERR_STREAM_PREMATURE_CLOSE errors on keep-alive sockets under Node.js v24.17.0+ (see nodejs/node#63989). Bump version to 3.4.2, sync package-lock.json, and update the changelog. --- README.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/dwolla/Auth.js | 1 + src/dwolla/Token.js | 3 +++ 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8a30339..28e73c5 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,8 @@ const response = await dwolla.post("customers", { ## Changelog +- **3.4.2** Disable automatic gzip handling on all `node-fetch` requests (pass `compress: false`) to avoid false-positive `ERR_STREAM_PREMATURE_CLOSE` errors on keep-alive sockets under Node.js v24.17.0+. See [nodejs/node#63989](https://github.com/nodejs/node/issues/63989). +- **3.4.1** Switch npm publishing to OIDC trusted publishing. - **[3.4.0](https://github.com/Dwolla/dwolla-v2-node/releases/tag/v3.4.0)** Update `form-urlencoded` version to allow `{ skipIndex: true, skipBracket: true }` options to be passed in. Thanks, [@MarcMouallem](https://github.com/MarcMouallem)! - **3.3.0** Remove `lodash` as a dependency in favor of `Object.assign` - **3.2.3** Update version and changelog diff --git a/package-lock.json b/package-lock.json index a12f04c..ec78d58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dwolla-v2", - "version": "3.4.0", + "version": "3.4.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "dwolla-v2", - "version": "3.4.0", + "version": "3.4.2", "license": "MIT", "dependencies": { "bluebird": "^3.5.1", diff --git a/package.json b/package.json index 54b6f00..fde4b44 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dwolla-v2", - "version": "3.4.1", + "version": "3.4.2", "description": "Dwolla V2 API client", "main": "src/index.js", "files": [ diff --git a/src/dwolla/Auth.js b/src/dwolla/Auth.js index 7c56391..4f778ed 100644 --- a/src/dwolla/Auth.js +++ b/src/dwolla/Auth.js @@ -36,6 +36,7 @@ function performOnGrantCallback(client, token) { function requestToken(client, params) { return fetch(client.tokenUrl, { method: "POST", + compress: false, headers: { "content-type": "application/x-www-form-urlencoded", "user-agent": require("../../src/dwolla/userAgent") diff --git a/src/dwolla/Token.js b/src/dwolla/Token.js index 81f657b..35f1351 100644 --- a/src/dwolla/Token.js +++ b/src/dwolla/Token.js @@ -78,6 +78,7 @@ function handleResponse(res) { Token.prototype.get = function(path, query, headers) { return fetch(getUrl(this, path, query), { + compress: false, headers: getHeaders(this, headers) }).then(handleResponse); }; @@ -85,6 +86,7 @@ Token.prototype.get = function(path, query, headers) { Token.prototype.post = function(path, body, headers) { return fetch(getUrl(this, path), { method: "POST", + compress: false, headers: assign( getHeaders(this, headers), isFormData(body) @@ -98,6 +100,7 @@ Token.prototype.post = function(path, body, headers) { Token.prototype.delete = function(path, query, headers) { return fetch(getUrl(this, path, query), { method: "DELETE", + compress: false, headers: getHeaders(this, headers) }).then(handleResponse); }; From b7c2f9ecaf7197e3d6259603574f98e672ddb9ce Mon Sep 17 00:00:00 2001 From: spencerhunter Date: Mon, 22 Jun 2026 20:25:43 -0500 Subject: [PATCH 2/2] Scope compress:false fix to the token endpoint only Customer reports (production and sandbox) show every failure occurs at the /token fetch before any downstream API call runs; the Token.js data calls were never reached. Revert compress:false there to preserve gzip on large API responses, and keep it only on the OAuth token request where the payload is tiny and compression provides no benefit. --- README.md | 2 +- src/dwolla/Token.js | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 28e73c5..fad6cfd 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ const response = await dwolla.post("customers", { ## Changelog -- **3.4.2** Disable automatic gzip handling on all `node-fetch` requests (pass `compress: false`) to avoid false-positive `ERR_STREAM_PREMATURE_CLOSE` errors on keep-alive sockets under Node.js v24.17.0+. See [nodejs/node#63989](https://github.com/nodejs/node/issues/63989). +- **3.4.2** Disable automatic gzip handling on the OAuth token request (pass `compress: false`) to avoid false-positive `ERR_STREAM_PREMATURE_CLOSE` errors on keep-alive sockets under Node.js v24.17.0+. See [nodejs/node#63989](https://github.com/nodejs/node/issues/63989). - **3.4.1** Switch npm publishing to OIDC trusted publishing. - **[3.4.0](https://github.com/Dwolla/dwolla-v2-node/releases/tag/v3.4.0)** Update `form-urlencoded` version to allow `{ skipIndex: true, skipBracket: true }` options to be passed in. Thanks, [@MarcMouallem](https://github.com/MarcMouallem)! - **3.3.0** Remove `lodash` as a dependency in favor of `Object.assign` diff --git a/src/dwolla/Token.js b/src/dwolla/Token.js index 35f1351..81f657b 100644 --- a/src/dwolla/Token.js +++ b/src/dwolla/Token.js @@ -78,7 +78,6 @@ function handleResponse(res) { Token.prototype.get = function(path, query, headers) { return fetch(getUrl(this, path, query), { - compress: false, headers: getHeaders(this, headers) }).then(handleResponse); }; @@ -86,7 +85,6 @@ Token.prototype.get = function(path, query, headers) { Token.prototype.post = function(path, body, headers) { return fetch(getUrl(this, path), { method: "POST", - compress: false, headers: assign( getHeaders(this, headers), isFormData(body) @@ -100,7 +98,6 @@ Token.prototype.post = function(path, body, headers) { Token.prototype.delete = function(path, query, headers) { return fetch(getUrl(this, path, query), { method: "DELETE", - compress: false, headers: getHeaders(this, headers) }).then(handleResponse); };