From 2e5c67f0ec4d60e8d9ba31929fbf595d030aaebf Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 2 Mar 2026 13:17:37 +0100 Subject: [PATCH 1/3] Add reproduction for sentry-javascript#19580 Co-Authored-By: Claude Opus 4.6 --- .../19580/.react-router/types/+future.ts | 9 +++ .../19580/.react-router/types/+routes.ts | 43 ++++++++++ .../.react-router/types/+server-build.d.ts | 18 +++++ .../.react-router/types/app/+types/root.ts | 59 ++++++++++++++ .../types/app/routes/+types/home.ts | 62 +++++++++++++++ .../types/app/routes/+types/items.$id.ts | 62 +++++++++++++++ sentry-javascript/19580/README.md | 52 +++++++++++++ sentry-javascript/19580/app/entry.client.tsx | 20 +++++ sentry-javascript/19580/app/entry.server.tsx | 78 +++++++++++++++++++ sentry-javascript/19580/app/root.tsx | 23 ++++++ sentry-javascript/19580/app/routes.ts | 6 ++ sentry-javascript/19580/app/routes/home.tsx | 33 ++++++++ .../19580/app/routes/items.$id.tsx | 17 ++++ sentry-javascript/19580/package.json | 24 ++++++ .../19580/react-router.config.ts | 5 ++ sentry-javascript/19580/tsconfig.json | 15 ++++ sentry-javascript/19580/vite.config.ts | 6 ++ 17 files changed, 532 insertions(+) create mode 100644 sentry-javascript/19580/.react-router/types/+future.ts create mode 100644 sentry-javascript/19580/.react-router/types/+routes.ts create mode 100644 sentry-javascript/19580/.react-router/types/+server-build.d.ts create mode 100644 sentry-javascript/19580/.react-router/types/app/+types/root.ts create mode 100644 sentry-javascript/19580/.react-router/types/app/routes/+types/home.ts create mode 100644 sentry-javascript/19580/.react-router/types/app/routes/+types/items.$id.ts create mode 100644 sentry-javascript/19580/README.md create mode 100644 sentry-javascript/19580/app/entry.client.tsx create mode 100644 sentry-javascript/19580/app/entry.server.tsx create mode 100644 sentry-javascript/19580/app/root.tsx create mode 100644 sentry-javascript/19580/app/routes.ts create mode 100644 sentry-javascript/19580/app/routes/home.tsx create mode 100644 sentry-javascript/19580/app/routes/items.$id.tsx create mode 100644 sentry-javascript/19580/package.json create mode 100644 sentry-javascript/19580/react-router.config.ts create mode 100644 sentry-javascript/19580/tsconfig.json create mode 100644 sentry-javascript/19580/vite.config.ts diff --git a/sentry-javascript/19580/.react-router/types/+future.ts b/sentry-javascript/19580/.react-router/types/+future.ts new file mode 100644 index 0000000..7f4533c --- /dev/null +++ b/sentry-javascript/19580/.react-router/types/+future.ts @@ -0,0 +1,9 @@ +// Generated by React Router + +import "react-router"; + +declare module "react-router" { + interface Future { + v8_middleware: false + } +} \ No newline at end of file diff --git a/sentry-javascript/19580/.react-router/types/+routes.ts b/sentry-javascript/19580/.react-router/types/+routes.ts new file mode 100644 index 0000000..4566862 --- /dev/null +++ b/sentry-javascript/19580/.react-router/types/+routes.ts @@ -0,0 +1,43 @@ +// Generated by React Router + +import "react-router" + +declare module "react-router" { + interface Register { + pages: Pages + routeFiles: RouteFiles + routeModules: RouteModules + } +} + +type Pages = { + "/": { + params: {}; + }; + "/items/:id": { + params: { + "id": string; + }; + }; +}; + +type RouteFiles = { + "root.tsx": { + id: "root"; + page: "/" | "/items/:id"; + }; + "routes/home.tsx": { + id: "routes/home"; + page: "/"; + }; + "routes/items.$id.tsx": { + id: "routes/items.$id"; + page: "/items/:id"; + }; +}; + +type RouteModules = { + "root": typeof import("./app/root.tsx"); + "routes/home": typeof import("./app/routes/home.tsx"); + "routes/items.$id": typeof import("./app/routes/items.$id.tsx"); +}; \ No newline at end of file diff --git a/sentry-javascript/19580/.react-router/types/+server-build.d.ts b/sentry-javascript/19580/.react-router/types/+server-build.d.ts new file mode 100644 index 0000000..13792c1 --- /dev/null +++ b/sentry-javascript/19580/.react-router/types/+server-build.d.ts @@ -0,0 +1,18 @@ +// Generated by React Router + +declare module "virtual:react-router/server-build" { + import { ServerBuild } from "react-router"; + export const assets: ServerBuild["assets"]; + export const assetsBuildDirectory: ServerBuild["assetsBuildDirectory"]; + export const basename: ServerBuild["basename"]; + export const entry: ServerBuild["entry"]; + export const future: ServerBuild["future"]; + export const isSpaMode: ServerBuild["isSpaMode"]; + export const prerender: ServerBuild["prerender"]; + export const publicPath: ServerBuild["publicPath"]; + export const routeDiscovery: ServerBuild["routeDiscovery"]; + export const routes: ServerBuild["routes"]; + export const ssr: ServerBuild["ssr"]; + export const allowedActionOrigins: ServerBuild["allowedActionOrigins"]; + export const unstable_getCriticalCss: ServerBuild["unstable_getCriticalCss"]; +} \ No newline at end of file diff --git a/sentry-javascript/19580/.react-router/types/app/+types/root.ts b/sentry-javascript/19580/.react-router/types/app/+types/root.ts new file mode 100644 index 0000000..5bd414e --- /dev/null +++ b/sentry-javascript/19580/.react-router/types/app/+types/root.ts @@ -0,0 +1,59 @@ +// Generated by React Router + +import type { GetInfo, GetAnnotations } from "react-router/internal"; + +type Module = typeof import("../root.js") + +type Info = GetInfo<{ + file: "root.tsx", + module: Module +}> + +type Matches = [{ + id: "root"; + module: typeof import("../root.js"); +}]; + +type Annotations = GetAnnotations; + +export namespace Route { + // links + export type LinkDescriptors = Annotations["LinkDescriptors"]; + export type LinksFunction = Annotations["LinksFunction"]; + + // meta + export type MetaArgs = Annotations["MetaArgs"]; + export type MetaDescriptors = Annotations["MetaDescriptors"]; + export type MetaFunction = Annotations["MetaFunction"]; + + // headers + export type HeadersArgs = Annotations["HeadersArgs"]; + export type HeadersFunction = Annotations["HeadersFunction"]; + + // middleware + export type MiddlewareFunction = Annotations["MiddlewareFunction"]; + + // clientMiddleware + export type ClientMiddlewareFunction = Annotations["ClientMiddlewareFunction"]; + + // loader + export type LoaderArgs = Annotations["LoaderArgs"]; + + // clientLoader + export type ClientLoaderArgs = Annotations["ClientLoaderArgs"]; + + // action + export type ActionArgs = Annotations["ActionArgs"]; + + // clientAction + export type ClientActionArgs = Annotations["ClientActionArgs"]; + + // HydrateFallback + export type HydrateFallbackProps = Annotations["HydrateFallbackProps"]; + + // Component + export type ComponentProps = Annotations["ComponentProps"]; + + // ErrorBoundary + export type ErrorBoundaryProps = Annotations["ErrorBoundaryProps"]; +} \ No newline at end of file diff --git a/sentry-javascript/19580/.react-router/types/app/routes/+types/home.ts b/sentry-javascript/19580/.react-router/types/app/routes/+types/home.ts new file mode 100644 index 0000000..e49d62a --- /dev/null +++ b/sentry-javascript/19580/.react-router/types/app/routes/+types/home.ts @@ -0,0 +1,62 @@ +// Generated by React Router + +import type { GetInfo, GetAnnotations } from "react-router/internal"; + +type Module = typeof import("../home.js") + +type Info = GetInfo<{ + file: "routes/home.tsx", + module: Module +}> + +type Matches = [{ + id: "root"; + module: typeof import("../../root.js"); +}, { + id: "routes/home"; + module: typeof import("../home.js"); +}]; + +type Annotations = GetAnnotations; + +export namespace Route { + // links + export type LinkDescriptors = Annotations["LinkDescriptors"]; + export type LinksFunction = Annotations["LinksFunction"]; + + // meta + export type MetaArgs = Annotations["MetaArgs"]; + export type MetaDescriptors = Annotations["MetaDescriptors"]; + export type MetaFunction = Annotations["MetaFunction"]; + + // headers + export type HeadersArgs = Annotations["HeadersArgs"]; + export type HeadersFunction = Annotations["HeadersFunction"]; + + // middleware + export type MiddlewareFunction = Annotations["MiddlewareFunction"]; + + // clientMiddleware + export type ClientMiddlewareFunction = Annotations["ClientMiddlewareFunction"]; + + // loader + export type LoaderArgs = Annotations["LoaderArgs"]; + + // clientLoader + export type ClientLoaderArgs = Annotations["ClientLoaderArgs"]; + + // action + export type ActionArgs = Annotations["ActionArgs"]; + + // clientAction + export type ClientActionArgs = Annotations["ClientActionArgs"]; + + // HydrateFallback + export type HydrateFallbackProps = Annotations["HydrateFallbackProps"]; + + // Component + export type ComponentProps = Annotations["ComponentProps"]; + + // ErrorBoundary + export type ErrorBoundaryProps = Annotations["ErrorBoundaryProps"]; +} \ No newline at end of file diff --git a/sentry-javascript/19580/.react-router/types/app/routes/+types/items.$id.ts b/sentry-javascript/19580/.react-router/types/app/routes/+types/items.$id.ts new file mode 100644 index 0000000..15d74b3 --- /dev/null +++ b/sentry-javascript/19580/.react-router/types/app/routes/+types/items.$id.ts @@ -0,0 +1,62 @@ +// Generated by React Router + +import type { GetInfo, GetAnnotations } from "react-router/internal"; + +type Module = typeof import("../items.$id.js") + +type Info = GetInfo<{ + file: "routes/items.$id.tsx", + module: Module +}> + +type Matches = [{ + id: "root"; + module: typeof import("../../root.js"); +}, { + id: "routes/items.$id"; + module: typeof import("../items.$id.js"); +}]; + +type Annotations = GetAnnotations; + +export namespace Route { + // links + export type LinkDescriptors = Annotations["LinkDescriptors"]; + export type LinksFunction = Annotations["LinksFunction"]; + + // meta + export type MetaArgs = Annotations["MetaArgs"]; + export type MetaDescriptors = Annotations["MetaDescriptors"]; + export type MetaFunction = Annotations["MetaFunction"]; + + // headers + export type HeadersArgs = Annotations["HeadersArgs"]; + export type HeadersFunction = Annotations["HeadersFunction"]; + + // middleware + export type MiddlewareFunction = Annotations["MiddlewareFunction"]; + + // clientMiddleware + export type ClientMiddlewareFunction = Annotations["ClientMiddlewareFunction"]; + + // loader + export type LoaderArgs = Annotations["LoaderArgs"]; + + // clientLoader + export type ClientLoaderArgs = Annotations["ClientLoaderArgs"]; + + // action + export type ActionArgs = Annotations["ActionArgs"]; + + // clientAction + export type ClientActionArgs = Annotations["ClientActionArgs"]; + + // HydrateFallback + export type HydrateFallbackProps = Annotations["HydrateFallbackProps"]; + + // Component + export type ComponentProps = Annotations["ComponentProps"]; + + // ErrorBoundary + export type ErrorBoundaryProps = Annotations["ErrorBoundaryProps"]; +} \ No newline at end of file diff --git a/sentry-javascript/19580/README.md b/sentry-javascript/19580/README.md new file mode 100644 index 0000000..8ae19e3 --- /dev/null +++ b/sentry-javascript/19580/README.md @@ -0,0 +1,52 @@ +# Reproduction for sentry-javascript#19580 + +**Issue:** https://github.com/getsentry/sentry-javascript/issues/19580 + +## Description + +`reactRouterTracingIntegration` produces `[object Object]` as transaction name when navigating using a `` (or `navigate()`) with an object `to` prop, e.g.: + +```tsx + +``` + +The root cause is in the patched `navigate` function which uses `String(args[0])` to derive the transaction name. When called with an object, `String({...})` produces `"[object Object]"`. + +## Steps to Reproduce + +1. Set your Sentry DSN: + ```bash + export SENTRY_DSN= + ``` + +2. Install dependencies: + ```bash + npm install + ``` + +3. Start the dev server: + ```bash + npm run dev + ``` + +4. Open the app in your browser (http://localhost:5173) + +5. Click **"Link with object to={{ pathname: "/items/2", search: "..." }}"** — this navigates using an object `to` prop + +6. Check Sentry: the transaction name for this navigation will be `[object Object]` + +7. For comparison, click **"Link with string to="/items/1?redirectTo=%2F""** — this navigates using a string `to` prop and the transaction name is correct + +## Expected Behavior + +Transaction name should be the resolved pathname (e.g., `/items/2`), not `[object Object]`. + +## Actual Behavior + +Transaction name is `[object Object]` when navigating with an object `to` prop. + +## Environment + +- `@sentry/react-router`: ^9.38.0 +- React Router: v7 (framework mode) +- React: 19 diff --git a/sentry-javascript/19580/app/entry.client.tsx b/sentry-javascript/19580/app/entry.client.tsx new file mode 100644 index 0000000..9d34774 --- /dev/null +++ b/sentry-javascript/19580/app/entry.client.tsx @@ -0,0 +1,20 @@ +import * as Sentry from "@sentry/react-router"; +import { startTransition, StrictMode } from "react"; +import { hydrateRoot } from "react-dom/client"; +import { HydratedRouter } from "react-router/dom"; + +Sentry.init({ + dsn: process.env.SENTRY_DSN || "", + integrations: [Sentry.browserTracingIntegration()], + tracesSampleRate: 1.0, + debug: true, // Enable debug to see transaction names in console +}); + +startTransition(() => { + hydrateRoot( + document, + + + + ); +}); diff --git a/sentry-javascript/19580/app/entry.server.tsx b/sentry-javascript/19580/app/entry.server.tsx new file mode 100644 index 0000000..27bb000 --- /dev/null +++ b/sentry-javascript/19580/app/entry.server.tsx @@ -0,0 +1,78 @@ +import * as Sentry from "@sentry/react-router"; +import type { RenderToPipeableStreamOptions } from "react-dom/server"; +import { renderToPipeableStream } from "react-dom/server"; +import type { AppLoadContext, EntryContext } from "react-router"; +import { ServerRouter } from "react-router"; + +Sentry.init({ + dsn: process.env.SENTRY_DSN || "", + tracesSampleRate: 1.0, +}); + +export default function handleRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + routerContext: EntryContext, + _loadContext: AppLoadContext +) { + return new Promise((resolve, reject) => { + let shellRendered = false; + + const { pipe, abort } = renderToPipeableStream( + , + { + onShellReady() { + shellRendered = true; + const body = new ReadableStream({ + start(controller) { + const encoder = new TextEncoder(); + pipe({ + write(chunk: string | Buffer) { + controller.enqueue( + typeof chunk === "string" ? encoder.encode(chunk) : chunk + ); + }, + end() { + controller.close(); + }, + on() {}, + off() {}, + removeListener() {}, + addListener() {}, + once() {}, + emit() { return false; }, + prependListener() { return this; }, + prependOnceListener() { return this; }, + listeners() { return []; }, + rawListeners() { return []; }, + listenerCount() { return 0; }, + eventNames() { return []; }, + getMaxListeners() { return 0; }, + setMaxListeners() { return this; }, + } as any); + }, + }); + responseHeaders.set("Content-Type", "text/html"); + resolve( + new Response(body, { + headers: responseHeaders, + status: responseStatusCode, + }) + ); + }, + onShellError(error: unknown) { + reject(error); + }, + onError(error: unknown) { + responseStatusCode = 500; + if (shellRendered) { + console.error(error); + } + }, + } as RenderToPipeableStreamOptions + ); + + setTimeout(abort, 5000); + }); +} diff --git a/sentry-javascript/19580/app/root.tsx b/sentry-javascript/19580/app/root.tsx new file mode 100644 index 0000000..11d5972 --- /dev/null +++ b/sentry-javascript/19580/app/root.tsx @@ -0,0 +1,23 @@ +import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router"; + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + + {children} + + + + + ); +} + +export default function App() { + return ; +} diff --git a/sentry-javascript/19580/app/routes.ts b/sentry-javascript/19580/app/routes.ts new file mode 100644 index 0000000..d55ed6c --- /dev/null +++ b/sentry-javascript/19580/app/routes.ts @@ -0,0 +1,6 @@ +import { type RouteConfig, index, route } from "@react-router/dev/routes"; + +export default [ + index("routes/home.tsx"), + route("items/:id", "routes/items.$id.tsx"), +] satisfies RouteConfig; diff --git a/sentry-javascript/19580/app/routes/home.tsx b/sentry-javascript/19580/app/routes/home.tsx new file mode 100644 index 0000000..144aaaf --- /dev/null +++ b/sentry-javascript/19580/app/routes/home.tsx @@ -0,0 +1,33 @@ +import { Link } from "react-router"; + +export default function Home() { + return ( +
+

Reproduction: sentry-javascript#19580

+

+ reactRouterTracingIntegration produces{" "} + [object Object] as transaction name when navigating with an + object to prop. +

+ +

Navigate with string (works correctly)

+

+ + Link with string to="/items/1?redirectTo=%2F" + +

+ +

Navigate with object (BUG: transaction name is [object Object])

+

+ + Link with object to={{ pathname: "/items/2", search: "..." }} + +

+
+ ); +} diff --git a/sentry-javascript/19580/app/routes/items.$id.tsx b/sentry-javascript/19580/app/routes/items.$id.tsx new file mode 100644 index 0000000..1abaa60 --- /dev/null +++ b/sentry-javascript/19580/app/routes/items.$id.tsx @@ -0,0 +1,17 @@ +import { Link, useParams, useSearchParams } from "react-router"; + +export default function ItemDetail() { + const { id } = useParams(); + const [searchParams] = useSearchParams(); + const redirectTo = searchParams.get("redirectTo") || "/"; + + return ( +
+

Item {id}

+

redirectTo: {redirectTo}

+

+ Back to home +

+
+ ); +} diff --git a/sentry-javascript/19580/package.json b/sentry-javascript/19580/package.json new file mode 100644 index 0000000..30df4d1 --- /dev/null +++ b/sentry-javascript/19580/package.json @@ -0,0 +1,24 @@ +{ + "name": "sentry-javascript-19580-repro", + "private": true, + "type": "module", + "scripts": { + "dev": "react-router dev", + "build": "react-router build", + "start": "react-router-serve ./build/server/index.js" + }, + "dependencies": { + "@sentry/react-router": "^9.38.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-router": "^7.6.1" + }, + "devDependencies": { + "@react-router/dev": "^7.6.1", + "@react-router/serve": "^7.6.1", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "typescript": "^5.7.0", + "vite": "^6.0.0" + } +} diff --git a/sentry-javascript/19580/react-router.config.ts b/sentry-javascript/19580/react-router.config.ts new file mode 100644 index 0000000..e45e273 --- /dev/null +++ b/sentry-javascript/19580/react-router.config.ts @@ -0,0 +1,5 @@ +import type { Config } from "@react-router/dev/config"; + +export default { + ssr: true, +} satisfies Config; diff --git a/sentry-javascript/19580/tsconfig.json b/sentry-javascript/19580/tsconfig.json new file mode 100644 index 0000000..71c227c --- /dev/null +++ b/sentry-javascript/19580/tsconfig.json @@ -0,0 +1,15 @@ +{ + "include": ["app/**/*.ts", "app/**/*.tsx", "vite.config.ts", "react-router.config.ts"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "noEmit": true, + "types": ["vite/client"] + } +} diff --git a/sentry-javascript/19580/vite.config.ts b/sentry-javascript/19580/vite.config.ts new file mode 100644 index 0000000..7ffae05 --- /dev/null +++ b/sentry-javascript/19580/vite.config.ts @@ -0,0 +1,6 @@ +import { reactRouter } from "@react-router/dev/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [reactRouter()], +}); From 352f3f504cba5f250af2c5c014f5c414a1496454 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 2 Mar 2026 13:27:53 +0100 Subject: [PATCH 2/3] Follow Sentry manual setup guide for React Router - Use reactRouterTracingIntegration() instead of browserTracingIntegration() - Use createSentryHandleRequest/createSentryHandleError in entry.server.tsx - Add instrument.server.mjs with NODE_OPTIONS for server-side init Co-Authored-By: Claude Opus 4.6 --- sentry-javascript/19580/app/entry.client.tsx | 2 +- sentry-javascript/19580/app/entry.server.tsx | 80 ++----------------- sentry-javascript/19580/instrument.server.mjs | 6 ++ sentry-javascript/19580/package.json | 7 +- 4 files changed, 19 insertions(+), 76 deletions(-) create mode 100644 sentry-javascript/19580/instrument.server.mjs diff --git a/sentry-javascript/19580/app/entry.client.tsx b/sentry-javascript/19580/app/entry.client.tsx index 9d34774..89b6a43 100644 --- a/sentry-javascript/19580/app/entry.client.tsx +++ b/sentry-javascript/19580/app/entry.client.tsx @@ -5,7 +5,7 @@ import { HydratedRouter } from "react-router/dom"; Sentry.init({ dsn: process.env.SENTRY_DSN || "", - integrations: [Sentry.browserTracingIntegration()], + integrations: [Sentry.reactRouterTracingIntegration()], tracesSampleRate: 1.0, debug: true, // Enable debug to see transaction names in console }); diff --git a/sentry-javascript/19580/app/entry.server.tsx b/sentry-javascript/19580/app/entry.server.tsx index 27bb000..a73718b 100644 --- a/sentry-javascript/19580/app/entry.server.tsx +++ b/sentry-javascript/19580/app/entry.server.tsx @@ -1,78 +1,14 @@ import * as Sentry from "@sentry/react-router"; -import type { RenderToPipeableStreamOptions } from "react-dom/server"; import { renderToPipeableStream } from "react-dom/server"; -import type { AppLoadContext, EntryContext } from "react-router"; import { ServerRouter } from "react-router"; +import { createReadableStreamFromReadable } from "@react-router/node"; -Sentry.init({ - dsn: process.env.SENTRY_DSN || "", - tracesSampleRate: 1.0, +export default Sentry.createSentryHandleRequest({ + ServerRouter, + renderToPipeableStream, + createReadableStreamFromReadable, }); -export default function handleRequest( - request: Request, - responseStatusCode: number, - responseHeaders: Headers, - routerContext: EntryContext, - _loadContext: AppLoadContext -) { - return new Promise((resolve, reject) => { - let shellRendered = false; - - const { pipe, abort } = renderToPipeableStream( - , - { - onShellReady() { - shellRendered = true; - const body = new ReadableStream({ - start(controller) { - const encoder = new TextEncoder(); - pipe({ - write(chunk: string | Buffer) { - controller.enqueue( - typeof chunk === "string" ? encoder.encode(chunk) : chunk - ); - }, - end() { - controller.close(); - }, - on() {}, - off() {}, - removeListener() {}, - addListener() {}, - once() {}, - emit() { return false; }, - prependListener() { return this; }, - prependOnceListener() { return this; }, - listeners() { return []; }, - rawListeners() { return []; }, - listenerCount() { return 0; }, - eventNames() { return []; }, - getMaxListeners() { return 0; }, - setMaxListeners() { return this; }, - } as any); - }, - }); - responseHeaders.set("Content-Type", "text/html"); - resolve( - new Response(body, { - headers: responseHeaders, - status: responseStatusCode, - }) - ); - }, - onShellError(error: unknown) { - reject(error); - }, - onError(error: unknown) { - responseStatusCode = 500; - if (shellRendered) { - console.error(error); - } - }, - } as RenderToPipeableStreamOptions - ); - - setTimeout(abort, 5000); - }); -} +export const handleError = Sentry.createSentryHandleError({ + logErrors: false, +}); diff --git a/sentry-javascript/19580/instrument.server.mjs b/sentry-javascript/19580/instrument.server.mjs new file mode 100644 index 0000000..e40a8a9 --- /dev/null +++ b/sentry-javascript/19580/instrument.server.mjs @@ -0,0 +1,6 @@ +import * as Sentry from "@sentry/react-router"; + +Sentry.init({ + dsn: process.env.SENTRY_DSN || "", + tracesSampleRate: 1.0, +}); diff --git a/sentry-javascript/19580/package.json b/sentry-javascript/19580/package.json index 30df4d1..e476a29 100644 --- a/sentry-javascript/19580/package.json +++ b/sentry-javascript/19580/package.json @@ -3,15 +3,16 @@ "private": true, "type": "module", "scripts": { - "dev": "react-router dev", + "dev": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router dev", "build": "react-router build", - "start": "react-router-serve ./build/server/index.js" + "start": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js" }, "dependencies": { "@sentry/react-router": "^9.38.0", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-router": "^7.6.1" + "react-router": "^7.6.1", + "@react-router/node": "^7.6.1" }, "devDependencies": { "@react-router/dev": "^7.6.1", From ab51de90529cba5d906bc5af6738c3379752051c Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Mon, 2 Mar 2026 14:54:20 +0100 Subject: [PATCH 3/3] updates --- sentry-javascript/19580/app/entry.client.tsx | 16 ++++++- sentry-javascript/19580/app/routes/home.tsx | 42 +++++++++++++++---- sentry-javascript/19580/instrument.server.mjs | 7 ++++ sentry-javascript/19580/package.json | 10 ++--- 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/sentry-javascript/19580/app/entry.client.tsx b/sentry-javascript/19580/app/entry.client.tsx index 89b6a43..f6720f9 100644 --- a/sentry-javascript/19580/app/entry.client.tsx +++ b/sentry-javascript/19580/app/entry.client.tsx @@ -3,11 +3,25 @@ import { startTransition, StrictMode } from "react"; import { hydrateRoot } from "react-dom/client"; import { HydratedRouter } from "react-router/dom"; +if (process.env.SENTRY_DSN) { + console.log("SENTRY_DSN is set, initializing Sentry"); +} else { + console.log("SENTRY_DSN is not set, skipping Sentry initialization"); +} + Sentry.init({ dsn: process.env.SENTRY_DSN || "", integrations: [Sentry.reactRouterTracingIntegration()], tracesSampleRate: 1.0, - debug: true, // Enable debug to see transaction names in console + debug: true, + beforeSendTransaction(event) { + const name = event.transaction; + console.log( + `[Sentry Transaction] name: "${name}", source: "${event.transaction_info?.source}"`, + name === "[object Object]" ? " <-- BUG (issue #19580)" : "" + ); + return event; + }, }); startTransition(() => { diff --git a/sentry-javascript/19580/app/routes/home.tsx b/sentry-javascript/19580/app/routes/home.tsx index 144aaaf..ab27756 100644 --- a/sentry-javascript/19580/app/routes/home.tsx +++ b/sentry-javascript/19580/app/routes/home.tsx @@ -1,23 +1,30 @@ -import { Link } from "react-router"; +import { Link, useNavigate } from "react-router"; export default function Home() { + const navigate = useNavigate(); + return (

Reproduction: sentry-javascript#19580

- reactRouterTracingIntegration produces{" "} - [object Object] as transaction name when navigating with an - object to prop. + reactRouterTracingIntegration uses String(args[0]) for + the transaction name. When navigate(to) is called with an object{" "} + to, that becomes [object Object]. +

+

+ Open the browser console and look for{" "} + [Sentry Transaction] name: "..." — when you navigate with an + object, the name should be [object Object] (the bug).

-

Navigate with string (works correctly)

+

1. Navigate with string (correct name)

- Link with string to="/items/1?redirectTo=%2F" + Link with string to="/items/1?redirectTo=%2F"

-

Navigate with object (BUG: transaction name is [object Object])

+

2. Navigate with object – Link (bug)

- Link with object to={{ pathname: "/items/2", search: "..." }} + Link with object to={{ pathname, search }}

+ +

3. Navigate with object – programmatic (bug; most reliable)

+

+ This calls navigate({ pathname, search }) directly, so + the router receives the object and Sentry does String(args[0]). +

+

+ +

); } diff --git a/sentry-javascript/19580/instrument.server.mjs b/sentry-javascript/19580/instrument.server.mjs index e40a8a9..606715a 100644 --- a/sentry-javascript/19580/instrument.server.mjs +++ b/sentry-javascript/19580/instrument.server.mjs @@ -1,6 +1,13 @@ import * as Sentry from "@sentry/react-router"; +if (process.env.SENTRY_DSN) { + console.log("SENTRY_DSN is set, initializing Sentry"); +} else { + console.log("SENTRY_DSN is not set, skipping Sentry initialization"); +} + Sentry.init({ dsn: process.env.SENTRY_DSN || "", tracesSampleRate: 1.0, + debug: false, }); diff --git a/sentry-javascript/19580/package.json b/sentry-javascript/19580/package.json index e476a29..84cb2e2 100644 --- a/sentry-javascript/19580/package.json +++ b/sentry-javascript/19580/package.json @@ -3,14 +3,14 @@ "private": true, "type": "module", "scripts": { - "dev": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router dev", - "build": "react-router build", - "start": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js" + "dev": "NODE_OPTIONS='--import ./instrument.server.mjs' NODE_ENV=production react-router dev", + "build": "NODE_ENV=production react-router build", + "start": "NODE_OPTIONS='--import ./instrument.server.mjs' NODE_ENV=production react-router-serve ./build/server/index.js" }, "dependencies": { "@sentry/react-router": "^9.38.0", - "react": "^19.0.0", - "react-dom": "^19.0.0", + "react": "19.0.0", + "react-dom": "19.0.0", "react-router": "^7.6.1", "@react-router/node": "^7.6.1" },