From 01388c05c78d1018a0db6637bcc334a58c65f4dd Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Sat, 15 Aug 2026 10:43:27 +0000 Subject: [PATCH 1/4] fix(api-client): Prevent ZodError on Sentry API 400 in validateEvents --- packages/mcp-core/src/api-client/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp-core/src/api-client/client.ts b/packages/mcp-core/src/api-client/client.ts index 2642b50d5..20d2b0069 100644 --- a/packages/mcp-core/src/api-client/client.ts +++ b/packages/mcp-core/src/api-client/client.ts @@ -3387,7 +3387,7 @@ export class SentryApiService { apiPath`/organizations/${organizationSlug}/events/validate/` + `?${queryParams.toString()}`, undefined, - { ...opts, allowStatuses: [400] }, + opts, ); const body = await this.parseJsonResponse(response); return EventsValidationResponseSchema.parse(body); From 2b58c30f6f8412e0bdab80ad72496f7612e80f3f Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Sat, 15 Aug 2026 10:52:31 +0000 Subject: [PATCH 2/4] fix(api-client): Handle generic 400 API errors in validateEvents --- packages/mcp-core/src/api-client/client.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/mcp-core/src/api-client/client.ts b/packages/mcp-core/src/api-client/client.ts index 20d2b0069..a888b132a 100644 --- a/packages/mcp-core/src/api-client/client.ts +++ b/packages/mcp-core/src/api-client/client.ts @@ -3387,10 +3387,23 @@ export class SentryApiService { apiPath`/organizations/${organizationSlug}/events/validate/` + `?${queryParams.toString()}`, undefined, - opts, + { ...opts, allowStatuses: [400] }, ); const body = await this.parseJsonResponse(response); - return EventsValidationResponseSchema.parse(body); + try { + return EventsValidationResponseSchema.parse(body); + } catch (err) { + if (err instanceof z.ZodError) { + // The API returned a 400 with a generic error body (e.g. {detail: "..."}) + // rather than a structured validation result. Treat it as an API client error. + const detail = + body && typeof body === "object" && "detail" in body + ? String((body as { detail: unknown }).detail) + : "Invalid request"; + throw createApiError(detail, 400, body); + } + throw err; + } } private async fetchTraceItemAttributes( From 6a12ab54dcadcf69249f5a33aa7fee7f1db403ca Mon Sep 17 00:00:00 2001 From: "sentry[bot]" <39604003+sentry[bot]@users.noreply.github.com> Date: Sat, 15 Aug 2026 11:55:23 +0000 Subject: [PATCH 3/4] fix(api-client): Handle generic 400 responses in validateEvents --- packages/mcp-core/src/api-client/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mcp-core/src/api-client/client.ts b/packages/mcp-core/src/api-client/client.ts index a888b132a..4131c018d 100644 --- a/packages/mcp-core/src/api-client/client.ts +++ b/packages/mcp-core/src/api-client/client.ts @@ -3400,7 +3400,7 @@ export class SentryApiService { body && typeof body === "object" && "detail" in body ? String((body as { detail: unknown }).detail) : "Invalid request"; - throw createApiError(detail, 400, body); + throw createApiError(detail, 400, detail, body); } throw err; } From f8d7209185bca3e0b6e1993e220fbdfba4340e6f Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 16:24:20 +0000 Subject: [PATCH 4/4] fix(api-client): preserve unexpected successful response failures --- .../mcp-core/src/api-client/client.test.ts | 32 +++++++++++++++++++ packages/mcp-core/src/api-client/client.ts | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/mcp-core/src/api-client/client.test.ts b/packages/mcp-core/src/api-client/client.test.ts index dc8d3dfe1..6b2a8b708 100644 --- a/packages/mcp-core/src/api-client/client.test.ts +++ b/packages/mcp-core/src/api-client/client.test.ts @@ -3,6 +3,38 @@ import { http, HttpResponse } from "msw"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ConfigurationError } from "../errors"; import { SentryApiService } from "./client"; +import { ApiValidationError } from "./errors"; +import { z } from "zod"; + +describe("validateEvents error bodies", () => { + afterEach(() => mswServer.resetHandlers()); + + it.each([400, 200])( + "preserves error classification for HTTP %i", + async (status) => { + mswServer.use( + http.get( + "https://sentry.io/api/0/organizations/test-org/events/validate/", + () => HttpResponse.json({ detail: "Invalid query" }, { status }), + ), + ); + const api = new SentryApiService({ + host: "sentry.io", + accessToken: "test-token", + }); + const result = api.validateEvents({ organizationSlug: "test-org" }); + if (status === 400) { + await expect(result).rejects.toBeInstanceOf(ApiValidationError); + await expect(result).rejects.toMatchObject({ + status: 400, + detail: "Invalid query", + }); + } else { + await expect(result).rejects.toBeInstanceOf(z.ZodError); + } + }, + ); +}); describe("getIssueUrl", () => { it("should work with sentry.io", () => { diff --git a/packages/mcp-core/src/api-client/client.ts b/packages/mcp-core/src/api-client/client.ts index 4131c018d..85b1f4168 100644 --- a/packages/mcp-core/src/api-client/client.ts +++ b/packages/mcp-core/src/api-client/client.ts @@ -3393,7 +3393,7 @@ export class SentryApiService { try { return EventsValidationResponseSchema.parse(body); } catch (err) { - if (err instanceof z.ZodError) { + if (response.status === 400 && err instanceof z.ZodError) { // The API returned a 400 with a generic error body (e.g. {detail: "..."}) // rather than a structured validation result. Treat it as an API client error. const detail =