From e918f17077b4c33fa6749618e997974b3a46e30c Mon Sep 17 00:00:00 2001 From: Karl Fredriksson Date: Thu, 15 Oct 2026 07:02:47 +0200 Subject: [PATCH] fix(js-sdk): don't return the unchanged request/response from the API logger On Node the API client fetches through undici, whose Response is not globalThis.Response. openapi-fetch treats any value returned from a middleware hook as a replacement and requires it to be an instance of the global Response, so with a `logger` set every API call threw "onResponse: must return new Response() when modifying the response". Return nothing from the hooks, which openapi-fetch reads as "unchanged". Fixes #1986 Co-Authored-By: Claude Opus 5.5 --- .changeset/api-logger-foreign-response.md | 5 +++ packages/js-sdk/src/logs.ts | 8 +++-- packages/js-sdk/tests/logs.test.ts | 38 ++++++++++++++++++++++- 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 .changeset/api-logger-foreign-response.md diff --git a/.changeset/api-logger-foreign-response.md b/.changeset/api-logger-foreign-response.md new file mode 100644 index 0000000000..e4a9e31491 --- /dev/null +++ b/.changeset/api-logger-foreign-response.md @@ -0,0 +1,5 @@ +--- +'e2b': patch +--- + +Fix every API call failing on Node with "onResponse: must return new Response() when modifying the response" when a `logger` is passed. The API logging middleware no longer returns the unchanged request and response, which openapi-fetch rejected because undici's `Response` is not the global one. diff --git a/packages/js-sdk/src/logs.ts b/packages/js-sdk/src/logs.ts index d3ed59f21f..d52076885b 100644 --- a/packages/js-sdk/src/logs.ts +++ b/packages/js-sdk/src/logs.ts @@ -58,11 +58,15 @@ export function createRpcLogger(logger: Logger): Interceptor { } } +/** + * The hooks return nothing: openapi-fetch treats a returned value as a + * replacement and requires it to be an instance of the global `Request` / + * `Response`, which undici's responses on Node are not. + */ export function createApiLogger(logger: Logger): Middleware { return { async onRequest({ request }) { logger.info?.(`Request ${request.method} ${request.url}`) - return request }, async onResponse({ response }) { if (response.status >= 400) { @@ -70,8 +74,6 @@ export function createApiLogger(logger: Logger): Middleware { } else { logger.info?.('Response:', response.status, response.statusText) } - - return response }, } } diff --git a/packages/js-sdk/tests/logs.test.ts b/packages/js-sdk/tests/logs.test.ts index e22dc3dac0..384b5fa179 100644 --- a/packages/js-sdk/tests/logs.test.ts +++ b/packages/js-sdk/tests/logs.test.ts @@ -1,5 +1,7 @@ +import createClient from 'openapi-fetch' +import { Response as UndiciResponse } from 'undici' import { assert, describe, test } from 'vitest' -import { createRpcLogger } from '../src/logs' +import { createApiLogger, createRpcLogger } from '../src/logs' const req = { url: 'http://localhost:49983/process.Process/Start' } as any @@ -37,3 +39,37 @@ describe('createRpcLogger', () => { ]) }) }) + +describe('createApiLogger', () => { + // On Node the API client fetches through undici, whose Response is not the + // global one; openapi-fetch rejects any onResponse result that is not an + // instance of the global Response. + test('logs responses that are not instances of the global Response', async () => { + assert.notStrictEqual(UndiciResponse, globalThis.Response) + const logs: any[][] = [] + const client = createClient({ + baseUrl: 'http://localhost', + fetch: async () => + new UndiciResponse('{"message":"boom"}', { + status: 500, + statusText: 'Internal Server Error', + headers: { 'content-type': 'application/json' }, + }) as unknown as Response, + }) + client.use( + createApiLogger({ + info: (...args) => logs.push(['info', ...args]), + error: (...args) => logs.push(['error', ...args]), + }) + ) + + const res = await client.GET('/sandboxes' as any) + + assert.equal(res.response.status, 500) + assert.deepEqual(res.error, { message: 'boom' }) + assert.deepEqual(logs, [ + ['info', 'Request GET http://localhost/sandboxes'], + ['error', 'Response:', 500, 'Internal Server Error'], + ]) + }) +})