From fee01f2e1edfaeb82299977c077cde0cec8b3315 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Tue, 20 Oct 2026 13:37:04 +0000 Subject: [PATCH] fix(solidstart): Name server action requests from the root span MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Nitro integration now opens a child handler span for each route, so the active span inside a server function no longer carries `url.path`. Read the request attributes from the root `http.server` span instead so `/_server` requests are named after the server action again. Co-Authored-By: Jan VamPeer Stöcklmair --- .../server/withServerActionInstrumentation.ts | 6 ++-- .../withServerActionInstrumentation.test.ts | 31 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/solidstart/src/server/withServerActionInstrumentation.ts b/packages/solidstart/src/server/withServerActionInstrumentation.ts index 785ab1432f91..def7b1367962 100644 --- a/packages/solidstart/src/server/withServerActionInstrumentation.ts +++ b/packages/solidstart/src/server/withServerActionInstrumentation.ts @@ -1,6 +1,6 @@ import { handleCallbackErrors, SPAN_STATUS_ERROR } from '@sentry/core'; import { flushIfServerless } from '@sentry/core/server'; -import { captureException, getActiveSpan, spanToJSON, startSpan } from '@sentry/node'; +import { captureException, getActiveSpan, getRootSpan, spanToJSON, startSpan } from '@sentry/node'; import { isRedirect } from './utils'; import { SENTRY_SEGMENT_NAME_SOURCE, @@ -25,7 +25,9 @@ export async function withServerActionInstrumentation { expect(mockSpanSetAttribute).to.toHaveBeenCalledWith(SENTRY_SEGMENT_NAME_SOURCE, 'route'); }); + it('sets a server action name on the root span when the active span is a child span', async () => { + setCurrentClient(createMockNodeClient()); + + const rootSpan = SentryCore.startInactiveSpan({ + name: 'POST', + parentSpan: null, + attributes: { + 'url.path': '/_server', + 'http.request.method': 'POST', + [SENTRY_OP]: 'http.server', + }, + }); + const handlerSpan = SentryCore.startInactiveSpan({ + name: 'POST /**', + parentSpan: rootSpan, + attributes: { [SENTRY_OP]: 'handler' }, + }); + mockGetActiveSpan.mockReturnValue(handlerSpan); + + await serverActionGetPrefecture(); + + expect(spanToJSON(rootSpan)).toMatchObject({ + name: 'POST getPrefecture', + attributes: expect.objectContaining({ + 'http.route': 'getPrefecture', + [SENTRY_SEGMENT_NAME_SOURCE]: 'route', + }), + }); + expect(spanToJSON(handlerSpan).name).toBe('POST /**'); + }); + it('does not set a server action name if the active span had a non `/_server` `url.path`', async () => { const span = new SentryCore.SentrySpan(); span.setAttribute('url.path', '/users/5');