From fa866c5ea7e3e25966f3cb158eb8d8fd879cbe74 Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Thu, 3 Sep 2026 21:51:40 -0300 Subject: [PATCH] fix(observability): preserve HTTP error status --- src/observability/http-transaction.ts | 3 ++- .../observability/http-transaction.test.ts | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/observability/http-transaction.ts b/src/observability/http-transaction.ts index 8f3b1507..c6b224ab 100644 --- a/src/observability/http-transaction.ts +++ b/src/observability/http-transaction.ts @@ -1,4 +1,5 @@ import { setHttpStatus, spanToJSON, startSpan, type Span } from "@sentry/react"; +import { isHTTPError } from "ky"; const INTEGER_SEGMENT = /^\d+$/; const UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; @@ -101,7 +102,7 @@ export async function traceHttpOperation( return result; } catch (error) { if (!statusReported) { - reportStatus(500); + reportStatus(isHTTPError(error) ? error.response.status : 500); } throw error; } diff --git a/src/test/observability/http-transaction.test.ts b/src/test/observability/http-transaction.test.ts index e08b2209..c90bc9bf 100644 --- a/src/test/observability/http-transaction.test.ts +++ b/src/test/observability/http-transaction.test.ts @@ -11,6 +11,7 @@ vi.mock("@sentry/react", () => ({ })); import * as Sentry from "@sentry/react"; +import { HTTPError } from "ky"; import { normalizeHttpRoute, traceHttpOperation } from "@/observability/http-transaction"; const startSpanMock = vi.mocked(Sentry.startSpan); @@ -125,6 +126,23 @@ describe("traceHttpOperation", () => { expect(setHttpStatusMock).toHaveBeenCalledWith(capturedSpan, 204); }); + it("reports the Ky HTTPError response status instead of 500", async () => { + const httpError = new HTTPError( + new Response("Unauthorized", { status: 401 }), + new Request("https://api.example.test/x"), + {} as never, + ); + + await expect( + traceHttpOperation({ method: "GET", url: "/x", entryPoint: "fn" }, async () => { + throw httpError; + }), + ).rejects.toThrow(); + + expect(setHttpStatusMock).toHaveBeenCalledTimes(1); + expect(setHttpStatusMock).toHaveBeenCalledWith(capturedSpan, 401); + }); + it("reports status 500 when the callback throws", async () => { await expect( traceHttpOperation({ method: "GET", url: "/x", entryPoint: "fn" }, async () => {