-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
fix(tanstackstart-react): workerd/worker export conditions resolve to @sentry/node, breaking Cloudflare Workers #20038
Description
Is there an existing issue for this?
- I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
How do you use Sentry?
Sentry SaaS (sentry.io)
Which SDK are you using?
@sentry/tanstackstart-react
SDK Version
10.46.0
Framework Version
TanStack Start ~1.166 (React 19, SSR via Nitro ~3.0)
Link to Sentry event
No response
Reproduction Example/SDK Setup
The core claim — that the workerd/worker conditions resolve to a file that re-exports @sentry/node — is verifiable directly from the package metadata and source without any app:
- Inspect
packages/tanstackstart-react/package.json—workerd,worker, andnodeall resolve toindex.server.js - Inspect
build/esm/index.server.js— doesexport * from '@sentry/node'
For the runtime failure:
- Create a TanStack Start app deployed to Cloudflare Workers (Nitro
cloudflare-modulepreset,noExternals: true) - Install
@sentry/tanstackstart-react@10.46.0 - Import in
router.tsx(SSR entry):import * as Sentry from '@sentry/tanstackstart-react' - Set
ssr.resolve.conditions: ['workerd', 'worker', 'browser', 'import', 'module', 'default']in Vite config - Build and deploy — the Worker returns HTTP 500 with
"Cannot initialize ExportedHandler"
Because Nitro uses noExternals: true for Workers (no node_modules at runtime), the package is not externalized. Vite resolves it using ssr.resolve.conditions (not ssr.resolve.externalConditions), so the workerd condition takes effect and routes to index.server.js.
Steps to Reproduce
@sentry/tanstackstart-react advertises workerd and worker export conditions in its package.json, suggesting compatibility with Cloudflare Workers and similar edge runtimes. However, all three server-side conditions (workerd, worker, node) resolve to the exact same file — index.server.js — which re-exports @sentry/node.
The Node-orientation is not limited to the barrel re-export. The server source modules themselves import directly from @sentry/node:
src/server/index.ts— re-exports from@sentry/nodesrc/server/sdk.ts— imports@sentry/nodeforinitsrc/server/wrapFetchWithSentry.ts— imports from@sentry/nodesrc/server/middleware.ts— imports from@sentry/node
When bundled for Workers (where @sentry/tanstackstart-react is non-externalized and resolved under the workerd condition), this pulls the @sentry/node dependency tree — including @opentelemetry/instrumentation-undici and transitive node:* built-in imports — into the Worker bundle. While Cloudflare Workers with nodejs_compat do support many Node APIs to varying degrees, in our deployment this caused module evaluation failure: the Worker's default export became null, producing "Cannot initialize ExportedHandler" at runtime with no stack trace. The exact failure mechanism likely depends on which node:* shims are partial or missing for the given compatibility date, but the root issue is that Workers resolves to a Node SDK entry that was never designed for this runtime.
package.json exports (v10.46.0):
{
"exports": {
".": {
"workerd": {
"import": "./build/esm/index.server.js",
"require": "./build/cjs/index.server.js"
},
"worker": {
"import": "./build/esm/index.server.js",
"require": "./build/cjs/index.server.js"
},
"browser": {
"import": "./build/esm/index.client.js",
"require": "./build/cjs/index.client.js"
},
"node": {
"import": "./build/esm/index.server.js",
"require": "./build/cjs/index.server.js"
}
}
}
}workerd, worker, and node all resolve to index.server.js. Only browser resolves to index.client.js. The same export map is present on the develop branch, so this is a current packaging issue.
Expected Result
The workerd and worker export conditions should not resolve to code that re-exports @sentry/node.
Minimal fix — remove misleading conditions:
Remove the workerd and worker export conditions from package.json, since the package does not currently provide Workers-compatible server code. This is a packaging stopgap, not a fully non-breaking resolution:
- Fallback behavior is toolchain-dependent. Bundlers whose active condition set includes
browser(e.g., Vite with['workerd', 'worker', 'browser', ...]) will fall through toindex.client.js, which is safe. But this is not guaranteed across all resolvers — Node's package docs recommend adefaultbranch for unknown runtimes. Whether to adddefaultpointing to the client entry is a design call for the Sentry team. - Export surface change. The
browser/client entry exports stubs forwrapMiddlewaresWithSentry,sentryGlobalRequestMiddleware, andsentryGlobalFunctionMiddleware, but it does not exportwrapFetchWithSentry. Consumers who import that helper would get a build error. The sharedindex.types.tsstill re-exports the full server API surface, so TypeScript types and runtime exports would diverge for Workers consumers.
Despite these caveats, removing the conditions is strictly better than the current state: it stops actively directing Workers bundlers to Node code, and the practical impact is limited since the server helpers don't work on Workers anyway.
Longer-term — proper Workers entry (follow-up):
A proper fix would create Worker-specific server modules that use @sentry/cloudflare instead of @sentry/node. This is non-trivial since @sentry/node is imported directly by multiple server source files, not just the barrel. This would likely be a separate design effort, potentially tracked alongside JS-1388.
Actual Result
Worker crashes with "Cannot initialize ExportedHandler" — silent module evaluation failure, no stack trace, no useful output from wrangler tail.
| Condition resolved | Server entry | What gets bundled | Result on Workers |
|---|---|---|---|
workerd |
index.server.js |
@sentry/node + OpenTelemetry + node:* |
Silent crash |
worker |
index.server.js |
@sentry/node + OpenTelemetry + node:* |
Silent crash |
browser |
index.client.js |
@sentry/browser (no Node deps) |
Works |
node |
index.server.js |
@sentry/node |
Works (Node.js) |
Workaround: Replace @sentry/tanstackstart-react with @sentry/react for runtime imports. Both export init, ErrorBoundary, and tanstackRouterBrowserTracingIntegration. This preserves client-side Sentry but loses the TanStack Start server helpers. For server-side Workers tracking, use @sentry/cloudflare separately. The Vite plugin (@sentry/tanstackstart-react/vite) is build-time only and unaffected.
Related issues
- JS-1388 — Investigate TanStack Start React Cloudflare support
- #12620 — Sentry Cloudflare Workers SDK
- JS-348 — Support Next.js on Cloudflare Workers (OpenNext) (same pattern with
@sentry/nextjs) - JS-1481 — Cannot resolve
@sentry/nextjson Cloudflare Workers (same class of bug) - JS-377 — React Router v7 deployed to Cloudflare Workers
Additional context
Note: We are aware the SDK setup docs note this is still alpha and warn that the setup "does not currently work for Cloudflare deployments." This issue is specifically about the misleading workerd/worker export conditions — they signal bundler-level compatibility that doesn't exist, causing a silent failure mode that is very difficult to diagnose.
Environment details: Cloudflare Workers paid plan, compatibility date 2025-09-01, nodejs_compat enabled. Bundling via Vite 7 (Rolldown) with Nitro noExternals: true.
We observed platform-dependent behavior: macOS local builds sometimes tree-shook enough undici code for the Worker to boot, while Linux CI included the full transport (~900 KiB), causing module evaluation failure. This variance is secondary to the export-map mismatch but made diagnosis significantly harder.
Metadata
Metadata
Assignees
Labels
Fields
Give feedbackProjects
Status