From 9c70aa909cbba972cecfb1e1a558952fbce51e0e Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 15 Sep 2026 11:10:35 +0200 Subject: [PATCH] docs(remix): Fix manual setup and document Cloudflare Workers for v11 Split the handleError snippet into two tabs and add the missing @sentry/vite-plugin install, both of which broke the build. Document a working Remix on Cloudflare Workers setup, drop the v10-only --require start command, and update the Cloudflare Vite plugin page to the shipped top-level options. Refs SDK-1512 --- .../cloudflare/features/vite-plugin.mdx | 56 +--- .../frameworks/hydrogen-react-router.mdx | 6 + .../cloudflare/frameworks/hydrogen-remix.mdx | 6 + .../guides/cloudflare/frameworks/remix.mdx | 10 +- .../javascript/guides/cloudflare/index.mdx | 9 +- .../javascript.remix.mdx | 244 +++++++++++++++++- 6 files changed, 263 insertions(+), 68 deletions(-) diff --git a/docs/platforms/javascript/guides/cloudflare/features/vite-plugin.mdx b/docs/platforms/javascript/guides/cloudflare/features/vite-plugin.mdx index e760be1efdf98..6617f205bc5f9 100644 --- a/docs/platforms/javascript/guides/cloudflare/features/vite-plugin.mdx +++ b/docs/platforms/javascript/guides/cloudflare/features/vite-plugin.mdx @@ -13,7 +13,9 @@ description: "Learn how to use the Sentry Cloudflare Vite plugin to instrument b The Sentry Cloudflare Vite plugin (`sentryCloudflareVitePlugin`) instruments your Worker at build time. It can: 1. **Instrument bundled dependencies**: automatically instruments supported packages in your bundle (such as database clients like `mysql`) at build time, giving you more traces out of the box. -2. **Auto-instrument your Worker entry**: optionally wraps your default export with `Sentry.withSentry()`, and Durable Object, Workflow, and Agents SDK classes with the matching `instrument*WithSentry` helper at build time, so you don't need to modify your code. +2. **Auto-instrument your Worker entry**: wraps your default export with `Sentry.withSentry()`, and Durable Object, Workflow, and Agents SDK classes with the matching `instrument*WithSentry` helper at build time, so you don't need to modify your code. + +Both are on by default. **We recommend building your Cloudflare Worker with Vite and the `sentryCloudflareVitePlugin` plugin.** It's the most complete way to get tracing for bundled dependencies in the Workers runtime. If you already deploy with `wrangler` directly, see [Migrating From Wrangler](#migrating-from-wrangler). @@ -23,11 +25,11 @@ The Vite plugin ships with `@sentry/cloudflare`, so there's no extra package to ## Prerequisites -The plugin relies on Node.js APIs (`diagnostics_channel`) at runtime, so your Worker must have the `nodejs_compat` compatibility flag enabled. See Node.js Compatibility Entrypoint for setup. +The plugin relies on Node.js APIs (`diagnostics_channel`) at runtime, so your Worker must have the [`nodejs_compat` compatibility flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) enabled. Workers with a `compatibility_date` of `2026-08-04` or later have it enabled by default. ## Configure -Enable `useDiagnosticsChannelInjection` to trace supported bundled dependencies, and wrap your handler with `withSentry` as usual: +Add the plugin to your `vite.config.ts`. Supported bundled dependencies are traced without further configuration. If you wrap your handler with `withSentry` yourself, the plugin leaves your entry as it is: ```typescript {filename:vite.config.ts} import { cloudflare } from "@cloudflare/vite-plugin"; @@ -35,14 +37,7 @@ import { sentryCloudflareVitePlugin } from "@sentry/cloudflare/vite"; import { defineConfig } from "vite"; export default defineConfig({ - plugins: [ - cloudflare(), - sentryCloudflareVitePlugin({ - _experimental: { - useDiagnosticsChannelInjection: true, - }, - }), - ], + plugins: [cloudflare(), sentryCloudflareVitePlugin()], }); ``` @@ -63,27 +58,11 @@ export default Sentry.withSentry( ); ``` -### Auto-instrumentation (Experimental) +### Auto-Instrumentation -Alternatively, the plugin can wrap your Worker for you at build time, so you don't need `withSentry` in your code. Enable `autoInstrumentation` and the plugin reads your wrangler config (probing `wrangler.json`, `wrangler.jsonc`, and `wrangler.toml` at the Vite root, or the file set with [`wranglerConfigPath`](#options)) to find the entry point, Durable Objects, workflows, and Agents SDK classes. The plugin wraps Agents SDK classes (`Agent`, `AIChatAgent`, `McpAgent`) with `instrumentAgentWithSentry` (SDK version 10.69.0 or higher), which also gives them automatic conversation IDs (see Cloudflare Agents SDK). +The plugin can also wrap your Worker for you at build time, so you don't need `withSentry` in your code. It reads your wrangler config (probing `wrangler.json`, `wrangler.jsonc`, and `wrangler.toml` at the Vite root, or the file set with [`wranglerConfigPath`](#options)) to find the entry point, Durable Objects, workflows, and Agents SDK classes. The plugin wraps Agents SDK classes (`Agent`, `AIChatAgent`, `McpAgent`) with `instrumentAgentWithSentry` (SDK version 10.69.0 or higher), which also gives them automatic conversation IDs (see Cloudflare Agents SDK). -```typescript {filename:vite.config.ts} -import { cloudflare } from "@cloudflare/vite-plugin"; -import { sentryCloudflareVitePlugin } from "@sentry/cloudflare/vite"; -import { defineConfig } from "vite"; - -export default defineConfig({ - plugins: [ - cloudflare(), - sentryCloudflareVitePlugin({ - _experimental: { - autoInstrumentation: true, - useDiagnosticsChannelInjection: true, - }, - }), - ], -}); -``` +Auto-instrumentation is on by default. To wrap your Worker yourself instead, set [`autoInstrumentation`](#options) to `false`. With auto-instrumentation, you can optionally provide Sentry options via a co-located `instrument.server.*` file (`.ts`, `.mts`, `.js`, `.mjs`, or `.cjs`) next to your Worker entry. The plugin resolves this location from `main` in your wrangler config. For example, if `main` is `src/worker/index.ts`, place the file at `src/worker/instrument.server.ts`, not at the project root. Use `defineCloudflareOptions` for full type-checking: @@ -137,9 +116,6 @@ export default defineConfig({ cloudflare({ configPath: "./wrangler.agent.jsonc" }), sentryCloudflareVitePlugin({ wranglerConfigPath: "./wrangler.agent.jsonc", - _experimental: { - autoInstrumentation: true, - }, }), ], }); @@ -147,21 +123,15 @@ export default defineConfig({ - - -Experimental options that may change or be removed without notice. - - - - + -Automatically wraps your Worker at build time so you don't have to edit your entry. The plugin reads your wrangler config, wraps the default export with `Sentry.withSentry()` (sourcing options from a co-located `instrument.server.*` file, falling back to `env`), and wraps configured classes with the matching helper: Durable Objects with `instrumentDurableObjectWithSentry`, Workflows with `instrumentWorkflowWithSentry`, and Agents SDK classes with `instrumentAgentWithSentry` (SDK version 10.69.0 or higher). Both `vite build` and `vite dev` are instrumented. The plugin also adds the bindings that resolve to the wrapped classes to `rpcTracePropagationBindings` (SDK version 10.72.0 or higher). +Instruments supported dependencies at build time. The plugin injects `diagnostics_channel` calls into bundled packages during both `vite build` and `vite dev`, so the SDK can trace them without runtime monkey-patching. Set to `false` to opt out. - + -Enables build-time automatic instrumentation of supported dependencies. When enabled, the plugin injects `diagnostics_channel` calls into bundled packages during both `vite build` and `vite dev`. When disabled or omitted, the plugin is a no-op. +Automatically wraps your Worker at build time so you don't have to edit your entry. The plugin reads your wrangler config, wraps the default export with `Sentry.withSentry()` (sourcing options from a co-located `instrument.server.*` file, falling back to `env`), and wraps configured classes with the matching helper: Durable Objects with `instrumentDurableObjectWithSentry`, Workflows with `instrumentWorkflowWithSentry`, and Agents SDK classes with `instrumentAgentWithSentry` (SDK version 10.69.0 or higher). Entries you already wrapped yourself are left untouched. Both `vite build` and `vite dev` are instrumented. The plugin also adds the bindings that resolve to the wrapped classes to `rpcTracePropagationBindings` (SDK version 10.72.0 or higher). Set to `false` to opt out. diff --git a/docs/platforms/javascript/guides/cloudflare/frameworks/hydrogen-react-router.mdx b/docs/platforms/javascript/guides/cloudflare/frameworks/hydrogen-react-router.mdx index 7e81d4baf69ba..5e7785e678e95 100644 --- a/docs/platforms/javascript/guides/cloudflare/frameworks/hydrogen-react-router.mdx +++ b/docs/platforms/javascript/guides/cloudflare/frameworks/hydrogen-react-router.mdx @@ -180,6 +180,12 @@ On Shopify Oxygen, never import anything directly from `@sentry/cloudflare`. Onl + + +`wrapRequestHandler` doesn't isolate requests from each other. Scope data you set during a request, for example with `setTag` or `setUser`, can show up on events from other concurrent or later requests in the same isolate. Use `wrapRequestHandler` only on runtimes that can't enable `nodejs_compat`, such as Shopify Oxygen. On Cloudflare Workers, wrap your handler with `Sentry.withSentry()` instead, as described in the [Cloudflare guide](/platforms/javascript/guides/cloudflare/). + + + diff --git a/docs/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix.mdx b/docs/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix.mdx index 0ae565165243d..0bc2d4d382e54 100644 --- a/docs/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix.mdx +++ b/docs/platforms/javascript/guides/cloudflare/frameworks/hydrogen-remix.mdx @@ -37,6 +37,12 @@ On Shopify Oxygen, never import anything directly from `@sentry/cloudflare`. Onl + + +`wrapRequestHandler` doesn't isolate requests from each other. Scope data you set during a request, for example with `setTag` or `setUser`, can show up on events from other concurrent or later requests in the same isolate. Use `wrapRequestHandler` only on runtimes that can't enable `nodejs_compat`, such as Shopify Oxygen. For Remix on Cloudflare Workers, follow the [Remix on Cloudflare guide](/platforms/javascript/guides/cloudflare/frameworks/remix/) instead. + + + ```ts {filename:server.ts} import { wrapRequestHandler } from "@sentry/cloudflare/request"; import { instrumentBuild } from "@sentry/remix/cloudflare"; diff --git a/docs/platforms/javascript/guides/cloudflare/frameworks/remix.mdx b/docs/platforms/javascript/guides/cloudflare/frameworks/remix.mdx index e67065e7a1fcc..87b9c85bf2122 100644 --- a/docs/platforms/javascript/guides/cloudflare/frameworks/remix.mdx +++ b/docs/platforms/javascript/guides/cloudflare/frameworks/remix.mdx @@ -31,21 +31,23 @@ Choose the features you want to configure, and this guide will show you how: -Run the command for your preferred package manager to add the Sentry SDK to your application: +Run the command for your preferred package manager to add the Sentry Remix and Cloudflare SDKs to your application. + +With npm, add `--legacy-peer-deps`: `@remix-run/cloudflare` requires `@cloudflare/workers-types` version 4, while Wrangler 4 requires version 5. ```bash {tabTitle:npm} -npm install @sentry/remix --save +npm install @sentry/remix @sentry/cloudflare --save --legacy-peer-deps ``` ```bash {tabTitle:yarn} -yarn add @sentry/remix +yarn add @sentry/remix @sentry/cloudflare ``` ```bash {tabTitle:pnpm} -pnpm add @sentry/remix +pnpm add @sentry/remix @sentry/cloudflare ``` diff --git a/docs/platforms/javascript/guides/cloudflare/index.mdx b/docs/platforms/javascript/guides/cloudflare/index.mdx index 89c95bda87586..3585e78970fa8 100644 --- a/docs/platforms/javascript/guides/cloudflare/index.mdx +++ b/docs/platforms/javascript/guides/cloudflare/index.mdx @@ -80,14 +80,7 @@ import { sentryCloudflareVitePlugin } from "@sentry/cloudflare/vite"; import { defineConfig } from "vite"; export default defineConfig({ - plugins: [ - cloudflare(), - sentryCloudflareVitePlugin({ - _experimental: { - useDiagnosticsChannelInjection: true, - }, - }), - ], + plugins: [cloudflare(), sentryCloudflareVitePlugin()], }); ``` diff --git a/platform-includes/getting-started-complete/javascript.remix.mdx b/platform-includes/getting-started-complete/javascript.remix.mdx index 373689d8ec566..d31428ac10375 100644 --- a/platform-includes/getting-started-complete/javascript.remix.mdx +++ b/platform-includes/getting-started-complete/javascript.remix.mdx @@ -186,8 +186,8 @@ Sentry.init({ profileSessionSampleRate: 1.0, // ___PRODUCT_OPTION_END___ profiling - // Optionally capture action formData attributes with errors. - // This requires `sendDefaultPii` set to true as well. + // Optionally limit which action formData fields are captured. + // Without this option, all fields are captured. captureActionFormDataKeys: { key_x: true, key_y: true, @@ -208,8 +208,6 @@ Then run your Remix server using the `--import` command line option and point it ```bash NODE_OPTIONS='--import=./instrument.server.mjs' remix-serve build -# or -NODE_OPTIONS='--require=./instrument.server.cjs' remix-serve build ``` @@ -227,10 +225,9 @@ If you use the Express server instead of the built-in Remix server, you can impo -```typescript {filename: server.(mjs|cjs)} +```typescript {filename: server.mjs} // import the Sentry instrumentation file before anything else. import "./instrument.server.mjs"; -// alternatively `require('./instrument.server.cjs')` // ... @@ -253,6 +250,59 @@ const app = express(); +Create a Worker entry file, for example `server.ts` in your project's root folder, and set it as `main` in your wrangler config. + +In this file, instrument your Remix server build with `instrumentBuild` from `@sentry/remix/cloudflare`, pass it to `createRequestHandler`, and wrap the exported handler with `Sentry.withSentry()` from `@sentry/cloudflare`. `withSentry` isolates each request, so scope data like tags and users stays on the request that set it. + + + +Don't also wrap `createRequestHandler` with `makeWrappedCreateRequestHandler`. Combined with `withSentry`, it reports every request twice, as two `http.server` transactions in two different traces. + + + + + + +```typescript {filename:server.ts} +import { createRequestHandler } from "@remix-run/cloudflare"; +import * as Sentry from "@sentry/cloudflare"; +import { instrumentBuild } from "@sentry/remix/cloudflare"; +// The server build produced by `remix vite:build` +import * as remixBuild from "./build/server/index.js"; + +const handleRequest = createRequestHandler(instrumentBuild(remixBuild)); + +export default Sentry.withSentry( + (env) => ({ + dsn: "___PUBLIC_DSN___", + // ___PRODUCT_OPTION_START___ performance + + // Set tracesSampleRate to 1.0 to capture 100% + // of transactions for tracing. + // We recommend adjusting this value in production + // Learn more at + // https://docs.sentry.io/platforms/javascript/configuration/options/#traces-sample-rate + tracesSampleRate: 1.0, + // ___PRODUCT_OPTION_END___ performance + }), + { + async fetch(request, env, ctx) { + return handleRequest(request, { env, ctx }); + }, + } +); +``` + + + + + + + + + + + @@ -276,8 +326,8 @@ export const onRequest = [ tracesSampleRate: 1.0, // ___PRODUCT_OPTION_END___ performance - // Optionally capture action formData attributes with errors. - // This requires `sendDefaultPii` set to true as well. + // Optionally limit which action formData fields are captured. + // Without this option, all fields are captured. captureActionFormDataKeys: { key_x: true, key_y: true, @@ -291,15 +341,22 @@ export const onRequest = [ + + ### Capture Server-Side Errors + + -To automatically capture server-side errors, instrument the [`handleError`](https://remix.run/docs/en/main/file-conventions/entry.server#handleerror) function in your server entry point (`entry.server.tsx`). You can wrap your custom error handler with `wrapHandleErrorWithSentry` or directly use `sentryHandleError`: +To automatically capture server-side errors, export a [`handleError`](https://remix.run/docs/en/main/file-conventions/entry.server#handleerror) function from your server entry point (`entry.server.tsx`). Choose one of the two options: + +- Wrap your custom error handler with `wrapHandleErrorWithSentry`. +- Use `sentryHandleError` directly if you don't have a custom error handler. @@ -310,7 +367,7 @@ Sentry's Remix SDK automatically records your [`action`](https://remix.run/docs/ -```typescript {filename: entry.server.tsx} +```typescript {tabTitle:Custom Error Handler} {filename: entry.server.tsx} import * as Sentry from "@sentry/remix"; export const handleError = Sentry.wrapHandleErrorWithSentry( @@ -318,8 +375,11 @@ export const handleError = Sentry.wrapHandleErrorWithSentry( // Custom handleError implementation } ); +``` + +```typescript {tabTitle:Default Error Handler} {filename: entry.server.tsx} +import * as Sentry from "@sentry/remix"; -// Alternative: Use the Sentry utility function if you don't need to wrap a custom function export const handleError = Sentry.sentryHandleError; ``` @@ -327,6 +387,37 @@ export const handleError = Sentry.sentryHandleError; + + + + + + + + +To automatically capture server-side errors, export `sentryHandleError` as the [`handleError`](https://remix.run/docs/en/main/file-conventions/entry.server#handleerror) function from your server entry point (`entry.server.tsx`). Without this export, Remix handles errors thrown in loaders and actions itself, and Sentry doesn't receive them. + + + +Import from `@sentry/remix/cloudflare`, not from `@sentry/remix`. In a Cloudflare server build, `sentryHandleError` from `@sentry/remix` is `undefined`, and no errors are captured. + + + + + + +```typescript {filename: entry.server.tsx} +import { sentryHandleError } from "@sentry/remix/cloudflare"; + +export const handleError = sentryHandleError; +``` + + + + + + + ### Configure Cloudflare for Sentry @@ -338,13 +429,114 @@ export const handleError = Sentry.sentryHandleError; +### Configure Vite + + + + + + + +Add `sentryRemixVitePlugin` from `@sentry/remix/vite` to your `vite.config.ts`. The plugin: + +- Parameterizes client-side transaction names with your route patterns, for example `/users/:id`. +- Instruments supported server-side dependencies, such as database clients, at build time. + + + +You can skip this step. Client-side transaction names then use the URL instead of the route pattern. + + + + + + +```typescript {filename:vite.config.ts} {2, 10} +import { vitePlugin as remix } from "@remix-run/dev"; +import { sentryRemixVitePlugin } from "@sentry/remix/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [ + remix({ + // ... your Remix plugin options + }), + sentryRemixVitePlugin(), + ], +}); +``` + + + + + + + + + + + + + +Add two Sentry plugins to your `vite.config.ts`, next to the Remix plugin: + +- `sentryRemixVitePlugin` from `@sentry/remix/vite` parameterizes transaction names with your route patterns, for example `/users/:id`. +- `sentryCloudflareVitePlugin` from `@sentry/cloudflare/vite` instruments supported bundled dependencies at build time, so you get spans for database clients like `mysql`. Without it, database calls produce no spans and no warning. + +Set `ssr.target` to `webworker` and bundle the Sentry packages and any database client you want traced with `ssr.noExternal`. Setting `noExternal: true` bundles every dependency, which works too. + + + + +```typescript {filename:vite.config.ts} {2-3, 11-12, 14-17} +import { vitePlugin as remix } from "@remix-run/dev"; +import { sentryCloudflareVitePlugin } from "@sentry/cloudflare/vite"; +import { sentryRemixVitePlugin } from "@sentry/remix/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [ + remix({ + // ... your Remix plugin options + }), + sentryRemixVitePlugin(), + sentryCloudflareVitePlugin(), + ], + ssr: { + target: "webworker", + noExternal: ["mysql", /^@sentry\//, "@opentelemetry/api"], + }, +}); +``` + + + + + + + +Build your app with `remix vite:build`. Under Vite 6, a plain `vite build` only builds the client and skips `build/server`, without an error. + + + + +```bash +npx remix vite:build +``` + + + + + + + ### Add Readable Stack Traces With Source Maps (Optional) -To upload source maps for clear error stack traces, add your Sentry auth token, organization, and project slug in your `vite.config.ts` file: +To upload source maps for clear error stack traces, install the Sentry Vite plugin: @@ -356,9 +548,33 @@ alternative setup options. -```javascript {filename:vite.config.ts} {3, 10-17,20-23} +```bash {tabTitle:npm} +npm install @sentry/vite-plugin --save-dev +``` + +```bash {tabTitle:yarn} +yarn add @sentry/vite-plugin --dev +``` + +```bash {tabTitle:pnpm} +pnpm add @sentry/vite-plugin --save-dev +``` + + + + + + + +Then add `sentryVitePlugin` with your Sentry auth token, organization, and project slug to your `vite.config.ts` file, and enable source map generation. Keep the plugins from the previous step: + + + + +```javascript {filename:vite.config.ts} {4, 13-20, 23-26} import { defineConfig } from "vite"; import { vitePlugin as remix } from "@remix-run/dev"; +import { sentryRemixVitePlugin } from "@sentry/remix/vite"; import { sentryVitePlugin } from "@sentry/vite-plugin"; export default defineConfig({ @@ -366,6 +582,8 @@ export default defineConfig({ remix({ // ... your Remix plugin options }), + sentryRemixVitePlugin(), + // ... other plugins sentryVitePlugin({ // If you use .sentryclirc or environment variables, // you don't need to specify these options