From 9617c636994aab026f883332ca0897c7341057f3 Mon Sep 17 00:00:00 2001 From: Ayesha Waseem Date: Sun, 25 Oct 2026 04:55:38 +0500 Subject: [PATCH 1/3] fix(astro): Initialize the server SDK on the first endpoint request The server init was only imported through injectScript('page-ssr'), which Astro adds to page modules but not to API endpoints. On a server that had not rendered a page yet, getClient() was undefined, so the request middleware skipped the request and errors thrown by endpoints were dropped. The middleware is now registered through a virtual module that imports the server init before re-exporting onRequest from @sentry/astro/middleware. With a server init file, the init is that file, which the page-ssr injection imports as well. With the default generated snippet, the snippet lives in a virtual module that both the page-ssr injection and the middleware import. Either way the module is shared, so Sentry.init() still runs once and still runs ahead of a page's own imports. Cloudflare Workers is unchanged, because the Worker wrapper already initializes the SDK for every request. Netlify is unchanged too: its optional edge middleware is bundled separately and cannot be detected here, and running the init there fails on Deno. Fixes #25729 --- packages/astro/src/integration/index.ts | 2 +- .../astro/test/integration/index.files.test.ts | 18 ++++++++++++++++++ packages/astro/test/integration/index.test.ts | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/integration/index.ts b/packages/astro/src/integration/index.ts index 66b5de50861b..6aacc3f04d4a 100644 --- a/packages/astro/src/integration/index.ts +++ b/packages/astro/src/integration/index.ts @@ -248,7 +248,7 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => { if (isSSR && shouldAddMiddleware) { addMiddleware({ order: 'pre', - entrypoint: serverInitSnippet ? SENTRY_MIDDLEWARE_MODULE : '@sentry/astro/middleware', + entrypoint: serverInitSnippet && !isCloudflareWorkers ? SENTRY_MIDDLEWARE_MODULE : '@sentry/astro/middleware', }); } }, diff --git a/packages/astro/test/integration/index.files.test.ts b/packages/astro/test/integration/index.files.test.ts index 125333ede7be..05cf524543b8 100644 --- a/packages/astro/test/integration/index.files.test.ts +++ b/packages/astro/test/integration/index.files.test.ts @@ -37,4 +37,22 @@ describe('sentryAstro integration', () => { /^import ".*\/sentry.server.config.js"/, ); }); + + it('imports the default server init file from the middleware entrypoint', async () => { + const addMiddleware = vi.fn(); + const updateConfig = vi.fn(); + const setup = sentryAstro({}).hooks['astro:config:setup'] as (params: unknown) => Promise; + + await setup({ updateConfig, injectScript, addMiddleware, config: { output: 'server' } }); + + expect(addMiddleware).toHaveBeenCalledWith({ + order: 'pre', + entrypoint: 'virtual:@sentry/astro/middleware', + }); + + const plugin = updateConfig.mock.calls + .flatMap(([updatedConfig]) => updatedConfig.vite?.plugins ?? []) + .find((vitePlugin: { name?: string }) => vitePlugin?.name === 'sentry-astro-server-init'); + expect(plugin.load('\0virtual:@sentry/astro/server-init')).toMatch(/^import ".*\/sentry.server.config.js";$/); + }); }); diff --git a/packages/astro/test/integration/index.test.ts b/packages/astro/test/integration/index.test.ts index 62481501bd23..7e4c3a6ce370 100644 --- a/packages/astro/test/integration/index.test.ts +++ b/packages/astro/test/integration/index.test.ts @@ -690,6 +690,22 @@ describe('sentryAstro integration', () => { }); }); + it('keeps the default middleware entrypoint for the Cloudflare Workers adapter', async () => { + const addMiddleware = vi.fn(); + const setup = sentryAstro({ serverInitPath: 'my-server-init-path.js' }).hooks['astro:config:setup'] as (params: unknown) => Promise; + + await setup({ + ...baseConfigHookObject, + config: { output: 'server', adapter: { name: '@astrojs/cloudflare' } }, + addMiddleware, + updateConfig, + injectScript, + }); + + expect(addMiddleware).toHaveBeenCalledWith({ order: 'pre', entrypoint: '@sentry/astro/middleware' }); + expect(getServerInit(updateConfig)).toContain('my-server-init-path.js'); + }); + it('runs the server init once, from SSR pages and the middleware alike, so requests that render no page find a client', async () => { const integration = sentryAstro({}); const updateConfig = vi.fn(); From 455743fe48f957010d3afc5b2656eeb8d1a4c29d Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 28 Oct 2026 10:12:23 +0100 Subject: [PATCH 2/3] fix(astro): Skip server init in edge runtimes With `edgeMiddleware` enabled, Vercel and Netlify bundle the middleware into a separate edge function. Since the middleware now imports the server init, the Node SDK would be initialized in the edge runtime, where it can't run. Return early from `init()` there so the middleware stays a no-op, as before. This also drops the Netlify adapter exclusion, so default (non-edge) Netlify setups initialize the SDK on the first endpoint request too. Co-Authored-By: Claude Opus 5.5 (1M context) --- packages/astro/src/server/sdk.ts | 15 ++++++++++++ packages/astro/test/server/sdk.test.ts | 32 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/packages/astro/src/server/sdk.ts b/packages/astro/src/server/sdk.ts index 557c1b7812d7..98910a1db2fd 100644 --- a/packages/astro/src/server/sdk.ts +++ b/packages/astro/src/server/sdk.ts @@ -4,11 +4,21 @@ import { applySdkMetadata } from '@sentry/core'; import type { NodeClient, NodeOptions } from '@sentry/node'; import { init as initNodeSdk } from '@sentry/node'; +declare const EdgeRuntime: string | undefined; +declare const Deno: unknown; +declare const Netlify: unknown; + /** * * @param options */ export function init(options: NodeOptions): NodeClient | undefined { + // With `edgeMiddleware` enabled, Vercel and Netlify bundle our middleware (which imports the + // server init) into a separate edge function, where the Node SDK can't run. + if (isEdgeRuntime()) { + return undefined; + } + const opts = { ...options, }; @@ -26,3 +36,8 @@ export function init(options: NodeOptions): NodeClient | undefined { return initNodeSdk(opts); } + +function isEdgeRuntime(): boolean { + // Netlify also defines the `Netlify` global in its Node functions, so only Deno + Netlify is its edge. + return typeof EdgeRuntime === 'string' || (typeof Netlify !== 'undefined' && typeof Deno !== 'undefined'); +} diff --git a/packages/astro/test/server/sdk.test.ts b/packages/astro/test/server/sdk.test.ts index 6de7cab9f38e..e9d15f50cb7f 100644 --- a/packages/astro/test/server/sdk.test.ts +++ b/packages/astro/test/server/sdk.test.ts @@ -10,10 +10,42 @@ describe('Sentry server SDK', () => { describe('init', () => { afterEach(() => { vi.clearAllMocks(); + vi.unstubAllGlobals(); getMainCarrier().__SENTRY__ = undefined; }); + it("doesn't initialize the SDK in the Vercel Edge runtime", () => { + vi.stubGlobal('EdgeRuntime', 'edge-runtime'); + + expect(init({})).toBeUndefined(); + expect(nodeInit).not.toHaveBeenCalled(); + }); + + it("doesn't initialize the SDK in Netlify edge functions", () => { + vi.stubGlobal('Netlify', {}); + vi.stubGlobal('Deno', {}); + + expect(init({})).toBeUndefined(); + expect(nodeInit).not.toHaveBeenCalled(); + }); + + it('initializes the SDK in Netlify Node functions', () => { + vi.stubGlobal('Netlify', {}); + + init({}); + + expect(nodeInit).toHaveBeenCalledTimes(1); + }); + + it('initializes the SDK in Deno', () => { + vi.stubGlobal('Deno', {}); + + init({}); + + expect(nodeInit).toHaveBeenCalledTimes(1); + }); + it('adds Astro metadata to the SDK options', () => { expect(nodeInit).not.toHaveBeenCalled(); From e6db1b6ab951a5ece62235616587770e410d1da7 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 3 Nov 2026 14:48:10 +0100 Subject: [PATCH 3/3] style(astro): Format rebase conflict resolutions Co-Authored-By: OpenAI Codex --- packages/astro/src/integration/index.ts | 3 ++- packages/astro/test/integration/index.test.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/integration/index.ts b/packages/astro/src/integration/index.ts index 6aacc3f04d4a..76bae75d5973 100644 --- a/packages/astro/src/integration/index.ts +++ b/packages/astro/src/integration/index.ts @@ -248,7 +248,8 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => { if (isSSR && shouldAddMiddleware) { addMiddleware({ order: 'pre', - entrypoint: serverInitSnippet && !isCloudflareWorkers ? SENTRY_MIDDLEWARE_MODULE : '@sentry/astro/middleware', + entrypoint: + serverInitSnippet && !isCloudflareWorkers ? SENTRY_MIDDLEWARE_MODULE : '@sentry/astro/middleware', }); } }, diff --git a/packages/astro/test/integration/index.test.ts b/packages/astro/test/integration/index.test.ts index 7e4c3a6ce370..870c28dc5b30 100644 --- a/packages/astro/test/integration/index.test.ts +++ b/packages/astro/test/integration/index.test.ts @@ -692,7 +692,9 @@ describe('sentryAstro integration', () => { it('keeps the default middleware entrypoint for the Cloudflare Workers adapter', async () => { const addMiddleware = vi.fn(); - const setup = sentryAstro({ serverInitPath: 'my-server-init-path.js' }).hooks['astro:config:setup'] as (params: unknown) => Promise; + const setup = sentryAstro({ serverInitPath: 'my-server-init-path.js' }).hooks['astro:config:setup'] as ( + params: unknown, + ) => Promise; await setup({ ...baseConfigHookObject,