-
-
Notifications
You must be signed in to change notification settings - Fork 0
Reproduction for sentry-javascript#19580 #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // Generated by React Router | ||
|
|
||
| import "react-router"; | ||
|
|
||
| declare module "react-router" { | ||
| interface Future { | ||
| v8_middleware: false | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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"); | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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"]; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Info & { module: Module, matches: Matches }, false>; | ||
|
|
||
| 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"]; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Info & { module: Module, matches: Matches }, false>; | ||
|
|
||
| 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"]; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Info & { module: Module, matches: Matches }, false>; | ||
|
|
||
| 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"]; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 `<Link>` (or `navigate()`) with an object `to` prop, e.g.: | ||
|
|
||
| ```tsx | ||
| <Link to={{ pathname: "/items/2", search: "redirectTo=%2F" }}> | ||
| ``` | ||
|
|
||
| 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=<your-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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import * as Sentry from "@sentry/react-router"; | ||
| 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 || "", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Client-side
|
||
| integrations: [Sentry.reactRouterTracingIntegration()], | ||
| tracesSampleRate: 1.0, | ||
| 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(() => { | ||
| hydrateRoot( | ||
| document, | ||
| <StrictMode> | ||
| <HydratedRouter /> | ||
| </StrictMode> | ||
| ); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import * as Sentry from "@sentry/react-router"; | ||
| import { renderToPipeableStream } from "react-dom/server"; | ||
| import { ServerRouter } from "react-router"; | ||
| import { createReadableStreamFromReadable } from "@react-router/node"; | ||
|
|
||
| export default Sentry.createSentryHandleRequest({ | ||
| ServerRouter, | ||
| renderToPipeableStream, | ||
| createReadableStreamFromReadable, | ||
| }); | ||
|
|
||
| export const handleError = Sentry.createSentryHandleError({ | ||
| logErrors: false, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router"; | ||
|
|
||
| export function Layout({ children }: { children: React.ReactNode }) { | ||
| return ( | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charSet="utf-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
| <Meta /> | ||
| <Links /> | ||
| </head> | ||
| <body> | ||
| {children} | ||
| <ScrollRestoration /> | ||
| <Scripts /> | ||
| </body> | ||
| </html> | ||
| ); | ||
| } | ||
|
|
||
| export default function App() { | ||
| return <Outlet />; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; |


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generated
.react-router/typesfiles committed to repositoryLow Severity
The
.react-router/types/directory contains auto-generated files (marked// Generated by React Router) that React Router's documentation explicitly says to add to.gitignore. These files are regenerated on every dev/build run and aren't meant to be version-controlled. A.gitignorefile with.react-router/is missing from the reproduction.Additional Locations (2)
sentry-javascript/19580/.react-router/types/+routes.ts#L1-L43sentry-javascript/19580/.react-router/types/+server-build.d.ts#L1-L18