Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions sentry-javascript/19580/.react-router/types/+future.ts
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
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated .react-router/types files committed to repository

Low 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 .gitignore file with .react-router/ is missing from the reproduction.

Additional Locations (2)

Fix in Cursor Fix in Web

43 changes: 43 additions & 0 deletions sentry-javascript/19580/.react-router/types/+routes.ts
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");
};
18 changes: 18 additions & 0 deletions sentry-javascript/19580/.react-router/types/+server-build.d.ts
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"];
}
59 changes: 59 additions & 0 deletions sentry-javascript/19580/.react-router/types/app/+types/root.ts
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"];
}
52 changes: 52 additions & 0 deletions sentry-javascript/19580/README.md
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
34 changes: 34 additions & 0 deletions sentry-javascript/19580/app/entry.client.tsx
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 || "",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client-side process.env unavailable in Vite bundle

High Severity

process.env.SENTRY_DSN is not available in Vite client-side bundles. Vite only exposes env variables prefixed with VITE_ via import.meta.env, and does not define a global process object in the browser. This will cause a ReferenceError: process is not defined at runtime, preventing Sentry from initializing on the client — which means the browserTracingIntegration never activates and the reproduction cannot actually demonstrate the [object Object] transaction name bug it was created for.

Fix in Cursor Fix in Web

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>
);
});
14 changes: 14 additions & 0 deletions sentry-javascript/19580/app/entry.server.tsx
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,
});
23 changes: 23 additions & 0 deletions sentry-javascript/19580/app/root.tsx
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 />;
}
6 changes: 6 additions & 0 deletions sentry-javascript/19580/app/routes.ts
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;
Loading