From 4b09457995a40242ba90cc3e85298484566f7253 Mon Sep 17 00:00:00 2001 From: m0sa Date: Wed, 22 Oct 2025 23:44:13 +0200 Subject: [PATCH 1/7] adding null-body-status response test --- .../test/fetch.test.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts b/experimental/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts index 43d3eb6116a..883d5529655 100644 --- a/experimental/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts @@ -369,6 +369,9 @@ describe('fetch', () => { msw.http.get('/boom', () => { return new msw.HttpResponse(null, { status: 500 }); }), + msw.http.get('/null-body', () => { + return new msw.HttpResponse(null, { status: 204 }); + }), ], callback = () => fetch('/api/status.json'), config = {}, @@ -391,6 +394,20 @@ describe('fetch', () => { return { rootSpan, response }; }; + describe('null-bodied response', () => { + // https://chromium.googlesource.com/chromium/src/+/ac85ca2a9cb8c76a37f9d7a6c611c24114f1f05d/third_party/WebKit/Source/core/fetch/Response.cpp#106 + let response: Response | undefined; + beforeEach(async () => { + const result = await tracedFetch({ + callback: () => fetch('/null-body'), + }); + response = result.response; + }); + it('should be handled correctly', async () => { + assert.strictEqual(response?.status, 204); + }); + }); + describe('simple request', () => { let rootSpan: api.Span | undefined; let response: Response | undefined; From 6ffb16836cdd908498267b980ea4b41e304492f7 Mon Sep 17 00:00:00 2001 From: m0sa Date: Thu, 23 Oct 2025 00:40:57 +0200 Subject: [PATCH 2/7] handling null-body-status response gracefully --- .../opentelemetry-instrumentation-fetch/src/fetch.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts b/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts index 443d1b60c66..5637f7eeeda 100644 --- a/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts +++ b/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts @@ -507,8 +507,14 @@ export class FetchInstrumentation extends InstrumentationBase Date: Thu, 23 Oct 2025 09:56:32 +0200 Subject: [PATCH 3/7] adding changelog entry --- experimental/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index e81dec8e1d2..32e8a89e91d 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -13,6 +13,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2 * feat(instrumentation): allow error of safeExecuteInTheMiddleAsync to be async [#6032](https://github.com/open-telemetry/opentelemetry-js/pull/6032) @JPeer264 ### :bug: Bug Fixes +* fix(instrumentation-fetch): Handling null-body-status responses [#6037](https://github.com/open-telemetry/opentelemetry-js/pull/6037) @m0sa ### :books: Documentation From ca8f2ee35e30670dd1caeed478c345fe6858251e Mon Sep 17 00:00:00 2001 From: m0sa Date: Thu, 23 Oct 2025 11:53:17 +0200 Subject: [PATCH 4/7] md harder --- experimental/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/experimental/CHANGELOG.md b/experimental/CHANGELOG.md index 32e8a89e91d..20389f92764 100644 --- a/experimental/CHANGELOG.md +++ b/experimental/CHANGELOG.md @@ -13,6 +13,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2 * feat(instrumentation): allow error of safeExecuteInTheMiddleAsync to be async [#6032](https://github.com/open-telemetry/opentelemetry-js/pull/6032) @JPeer264 ### :bug: Bug Fixes + * fix(instrumentation-fetch): Handling null-body-status responses [#6037](https://github.com/open-telemetry/opentelemetry-js/pull/6037) @m0sa ### :books: Documentation From 32c82fdc996c6d67a9d60bedf2ec93bf29070d59 Mon Sep 17 00:00:00 2001 From: Samo Prelog Date: Fri, 24 Oct 2025 00:09:21 +0200 Subject: [PATCH 5/7] using correct assertion Co-authored-by: Wolfgang Therrien --- .../opentelemetry-instrumentation-fetch/test/fetch.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts b/experimental/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts index 883d5529655..b0501b5b55c 100644 --- a/experimental/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts +++ b/experimental/packages/opentelemetry-instrumentation-fetch/test/fetch.test.ts @@ -403,8 +403,8 @@ describe('fetch', () => { }); response = result.response; }); - it('should be handled correctly', async () => { - assert.strictEqual(response?.status, 204); + it('204 (No Content) will correctly end the span', async () => { + assert.strictEqual(exportedSpans.length, 1); }); }); From 20b8698f5230d64b4791f4c349a900a333ff7a62 Mon Sep 17 00:00:00 2001 From: m0sa Date: Fri, 24 Oct 2025 02:10:01 +0200 Subject: [PATCH 6/7] adding tests for other cases --- .../src/fetch.ts | 3 +- .../test/fetch.test.ts | 62 ++++++++++++++++--- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts b/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts index 5637f7eeeda..9138a49f347 100644 --- a/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts +++ b/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts @@ -508,7 +508,8 @@ export class FetchInstrumentation extends InstrumentationBase { msw.http.get('/boom', () => { return new msw.HttpResponse(null, { status: 500 }); }), - msw.http.get('/null-body', () => { + msw.http.get('/null-body-204', () => { return new msw.HttpResponse(null, { status: 204 }); }), + msw.http.get('/null-body-205', () => { + return new msw.HttpResponse(null, { status: 205 }); + }), + msw.http.get('/null-body-304', () => { + return new msw.HttpResponse(null, { status: 304 }); + }), ], callback = () => fetch('/api/status.json'), config = {}, @@ -396,15 +402,57 @@ describe('fetch', () => { describe('null-bodied response', () => { // https://chromium.googlesource.com/chromium/src/+/ac85ca2a9cb8c76a37f9d7a6c611c24114f1f05d/third_party/WebKit/Source/core/fetch/Response.cpp#106 - let response: Response | undefined; - beforeEach(async () => { - const result = await tracedFetch({ - callback: () => fetch('/null-body'), - }); - response = result.response; + it('101 (Switching Protocols) will correctly end the span', async () => { + await startWorker( + msw.http.get('/null-body-101', () => { + return new msw.HttpResponse(null, { status: 101 }); + }) + ); + try { + await trace( + async () => { + await fetch('/null-body-101'); + }, + { ignoreNetworkEvents: false }, + false + ); + } catch (err) {) + // fetch throws `TypeError: Failed to fetch` on 101 response + } + + // make sure we still have a span with the captured error + assert.strictEqual(exportedSpans.length, 1); + assert.match(exportedSpans[0].status.message ?? '', /TypeError/); }); it('204 (No Content) will correctly end the span', async () => { + await tracedFetch({ + callback: () => fetch('/null-body-204'), + }); + assert.strictEqual(exportedSpans.length, 1); + assert.strictEqual( + exportedSpans[0].attributes[ATTR_HTTP_STATUS_CODE], + 204 + ); + }); + it('205 (Reset Content) will correctly end the span', async () => { + await tracedFetch({ + callback: () => fetch('/null-body-205'), + }); assert.strictEqual(exportedSpans.length, 1); + assert.strictEqual( + exportedSpans[0].attributes[ATTR_HTTP_STATUS_CODE], + 205 + ); + }); + it('304 (Not Modified) will correctly end the span', async () => { + await tracedFetch({ + callback: () => fetch('/null-body-304'), + }); + assert.strictEqual(exportedSpans.length, 1); + assert.strictEqual( + exportedSpans[0].attributes[ATTR_HTTP_STATUS_CODE], + 304 + ); }); }); From 99e48372154e80638becd112ce06cfbec92ccb75 Mon Sep 17 00:00:00 2001 From: m0sa Date: Fri, 24 Oct 2025 15:37:14 +0200 Subject: [PATCH 7/7] removing 101 case --- .../src/fetch.ts | 3 +-- .../test/fetch.test.ts | 22 ------------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts b/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts index 9138a49f347..0328a229916 100644 --- a/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts +++ b/experimental/packages/opentelemetry-instrumentation-fetch/src/fetch.ts @@ -508,8 +508,7 @@ export class FetchInstrumentation extends InstrumentationBase { describe('null-bodied response', () => { // https://chromium.googlesource.com/chromium/src/+/ac85ca2a9cb8c76a37f9d7a6c611c24114f1f05d/third_party/WebKit/Source/core/fetch/Response.cpp#106 - it('101 (Switching Protocols) will correctly end the span', async () => { - await startWorker( - msw.http.get('/null-body-101', () => { - return new msw.HttpResponse(null, { status: 101 }); - }) - ); - try { - await trace( - async () => { - await fetch('/null-body-101'); - }, - { ignoreNetworkEvents: false }, - false - ); - } catch (err) {) - // fetch throws `TypeError: Failed to fetch` on 101 response - } - - // make sure we still have a span with the captured error - assert.strictEqual(exportedSpans.length, 1); - assert.match(exportedSpans[0].status.message ?? '', /TypeError/); - }); it('204 (No Content) will correctly end the span', async () => { await tracedFetch({ callback: () => fetch('/null-body-204'),