Repository navigation
[Bug]: JS SDK: passing logger makes every API call fail on Node with "onResponse: must return new Response() when modifying the response" #1986
Description
Sandbox ID or Build ID
None. The call fails before a sandbox is created.
Environment
e2b2.32.0 and 2.56.0 (latest). Both reproduce.- Node.js 24.14.1 (macOS and Linux/Alpine)
openapi-fetch0.14.1 (bundled with 2.32.0) and 0.17.0 (bundled with 2.56.0)undiciresolvable, which is always the case becausee2bdepends on it
Timestamp of the issue
2026-10-14 20:00 UTC onwards (when we started passing logger). It reproduces every time.
Frequency
Happens every time
Expected behavior
Passing a logger in the connection options (for example Sandbox.create({ logger })) only adds logging, and the request behaves as it does without one.
Actual behavior
On Node, every API call made through ApiClient throws before the response is handled:
Error: onResponse: must return new Response() when modifying the response
at coreFetch (node_modules/openapi-fetch/dist/index.mjs:...)
Without logger, the same call works.
Cause: On Node the SDK fetches through the npm undici (loadUndici() / buildApiFetcher in src/api/http2.ts), so responses are instances of undici.Response, not globalThis.Response. When a logger is set, createApiLogger (packages/js-sdk/src/logs.ts) installs a middleware whose onResponse returns the response it received. openapi-fetch treats any truthy onResponse result as a replacement response and requires result instanceof Response, checked against the global Response. undici.Response !== globalThis.Response, so the check fails.
Under Bun (and in browsers) the SDK uses the global fetch, so the classes match and the bug doesn't show. That's probably why it hasn't been noticed.
Suggested fix: don't return the unchanged response (or request). In openapi-fetch, returning undefined from a middleware means "unchanged":
export function createApiLogger(logger: Logger): Middleware {
return {
async onRequest({ request }) {
logger.info?.(`Request ${request.method} ${request.url}`)
},
async onResponse({ response }) {
if (response.status >= 400) {
logger.error?.('Response:', response.status, response.statusText)
} else {
logger.info?.('Response:', response.status, response.statusText)
}
},
}
}Reproduction
No E2B account needed; a local stub server stands in for the API. Run with node repro.mjs (Node 24, npm i e2b):
import http from 'node:http'
import { Sandbox } from 'e2b'
const server = http
.createServer((req, res) => {
res.writeHead(500, { 'content-type': 'application/json' })
res.end('{"code":500,"message":"stub api reached"}')
})
.listen(0)
await new Promise((r) => server.once('listening', r))
const apiUrl = `http://127.0.0.1:${server.address().port}`
const apiKey = 'e2b_' + '0'.repeat(40)
for (const logger of [undefined, { warn() {}, error() {} }]) {
try {
await Sandbox.create({ apiKey, apiUrl, logger })
} catch (e) {
console.log(logger ? 'with logger: ' : 'without logger:', e.message)
}
}
const undici = await import('undici')
console.log('undici.Response === globalThis.Response:', undici.Response === globalThis.Response)
server.close()Output:
without logger: 500: stub api reached
with logger: onResponse: must return new Response() when modifying the response
undici.Response === globalThis.Response: false
Under bun repro.mjs, both lines print 500: stub api reached.
Activity
devin-ai-integration commentedon Oct 15, 2026
Triage
createApiLoggerinsrc/logs.ts, undici fetcher insrc/api/http2.ts)loggerin connection options makes everyApiClientcall fail with "onResponse: must return new Response() when modifying the response", because the logger middleware returns the unchanged undiciResponse, which fails openapi-fetch'sinstanceof globalThis.Responsecheck. The issue includes a repro and a suggested fix (returnundefinedfromonRequest/onResponse).Written by Devin
CloudCorpRecords commentedon Oct 16, 2026
Hi, I would like to work on this. Plan: in the JS SDK, fix the openapi-fetch logging path so passing a custom logger does not break API calls: where the onResponse middleware modifies the response, return a new Response as the triage note suggests (createApiLogger in src/logs.ts, the fetcher in src/api/http2.ts), and add a regression test with a logger configured on Node.
CloudCorpRecords commentedon Oct 16, 2026
Withdrawing my claim: PR #1987 by KarlFredrikssonEthira already fixes this, and it opened before my claim did. My race check missed the cross-referenced PR. Thanks, and good luck landing it.