Skip to content

@sentry/astro: Cloudflare Worker server wrap not applied with @astrojs/cloudflare v14 (transform targets astrojs-ssr-virtual-entry, adapter now emits virtual:cloudflare/worker-entry) #21901

Closed

Description

@0x80

Summary

With @astrojs/cloudflare v14, @sentry/astro's server-side init is never applied to the deployed Cloudflare Worker. The Worker's default export is left un-wrapped by withSentry, so server-side error/log capture does not initialize at the fetch boundary — server-side Sentry silently does nothing.

Versions

  • @sentry/astro 10.62.0
  • @sentry/cloudflare 10.62.0
  • @astrojs/cloudflare 14.0.1
  • astro 7.0.3
  • output: 'server', Cloudflare Workers (not Pages)

Root cause

sentryCloudflareVitePlugin (packages/astro/src/integration/cloudflare.ts) wraps the Worker by transforming the module whose id contains astrojs-ssr-virtual-entry:

transform(code, id) {
  if (!id.includes("astrojs-ssr-virtual-entry")) {
    return undefined;
  }
  // ...rewrites `export default X;` -> `export default withSentry(() => undefined, X);`
}

@astrojs/cloudflare v14 builds the Worker through @cloudflare/vite-plugin, whose entry virtual module id is virtual:cloudflare/worker-entry (its generated body ends with export default mod.default ?? {};). The old astrojs-ssr-virtual-entry id is no longer produced, so the transform guard always returns early and the entry is never wrapped.

Effect

With an app configured per the Astro + Cloudflare docs (the sentry() integration in astro.config plus a sentry.server.config.ts doing export default Sentry.withSentry((env) => ({ ... }), handler)), the generated dist/server/entry.mjs default export is the raw Astro handler. Grepping all of dist/server for withSentry(() => finds no match — the plugin's wrap never ran.

The only remaining server wiring is the injected import "sentry.server.config.(ts|js)" (injectScript("page-ssr", ...)). That runs lazily during a page render, so it doesn't deterministically instrument the Worker's fetch entry:

  • errors on the first request (before any page module has evaluated the config) aren't captured;
  • routes that don't pull the page-ssr module graph (e.g. API endpoints) aren't covered;
  • for a Worker whose first traffic is an API route, Sentry.init may never run at all.

Repro

  1. Scaffold an Astro app with output: 'server' and the @astrojs/cloudflare v14 adapter (Workers, not Pages).
  2. npm i @sentry/astro @sentry/cloudflare; add sentry() to integrations; create sentry.server.config.ts per the docs:
    import * as Sentry from '@sentry/cloudflare'
    import handler from '@astrojs/cloudflare/entrypoints/server'
    export default Sentry.withSentry((env) => ({ dsn: env.SENTRY_DSN, enableLogs: true }), handler)
  3. astro build.
  4. Inspect dist/server/entry.mjs: the default export is the un-wrapped Astro handler, and grep -r "withSentry(() =>" dist/server returns nothing.

Suggested fix

Have sentryCloudflareVitePlugin's transform also match the @cloudflare/vite-plugin entry id virtual:cloudflare/worker-entry (whose default-export expression is mod.default ?? {}), in addition to astrojs-ssr-virtual-entry, so the wrap applies on @astrojs/cloudflare v14+.

Workaround

For anyone hitting this before a fix lands: a tiny build-only Vite plugin that transforms the virtual:cloudflare/worker-entry module and wraps its export default mod.default ?? {}; with your own withSentry(optionsCallback, handler) restores deterministic, eager wrapping of the Worker entry.

Activity

  1. linear-code commented on Jul 1, 2026

    @linear-code
  2. moved this to Waiting for: Product Owner in GitHub Issues with 👀 3on Jul 1, 2026
  3. JPeer264 commented on Jul 1, 2026

    @JPeer264
    Member

    How does your wrangler.jsonc look like? I think you might have missed the step where you change the entrypoint for wrangler:

    https://docs.sentry.io/platforms/javascript/guides/cloudflare/frameworks/astro/#configure-custom-entry-point

    {
      "main": "./sentry.server.config.ts",
    }
  4. moved this from Waiting for: Product Owner to No status in GitHub Issues with 👀 3on Jul 1, 2026
  5. self-assigned this
    on Jul 1, 2026
  6. moved this to Waiting for: Community in GitHub Issues with 👀 3on Jul 1, 2026
  7. 0x80 commented on Jul 1, 2026

    @0x80
    Author

    Thanks! I'm not using the custom entry point — my deploy uses the adapter's generated Worker entry (dist/server/entry.mjs) directly rather than pointing wrangler's main at a sentry.server.config.ts. Pointing main at a custom entry would sidestep the problem (that export does still exist on v14), but it's a different mechanism from the sentry() integration's automatic server wrap, which is what this issue is about.

    The integration still installs sentryCloudflareVitePlugin on v14, but its transform only matches an id containing astrojs-ssr-virtual-entry — which v14 no longer emits. v14 builds the Worker through @cloudflare/vite-plugin, whose entry is virtual:cloudflare/worker-entry (body ending export default mod.default ?? {};). So the guard returns early, the generated entry ships un-wrapped, and server-side Sentry.init never runs at the fetch boundary — silently, with no build warning.

    The custom entry point is a fine workaround, but an integration that installs a wrap matching nothing and capturing nothing is a footgun on its own. Two directions that would fix the actual regression:

    • Have the transform also match the v14 entry id virtual:cloudflare/worker-entry (default-export expression mod.default ?? {}), in addition to astrojs-ssr-virtual-entry, so the auto-wrap fires again on v14+; and/or
    • Warn/error at build time when the integration produced a Cloudflare Worker entry it couldn't wrap, so this fails loud instead of silently doing nothing.

    Happy to test a patch against @astrojs/cloudflare v14 if that helps.

  8. moved this from Waiting for: Community to Waiting for: Product Owner in GitHub Issues with 👀 3on Jul 1, 2026
  9. JPeer264 commented on Jul 2, 2026

    @JPeer264
    Member

    Yes, astrojs-ssr-virtual-entry was actually for v12 and this got removed in v13+. I'll check further with the virtual:cloudflare/worker-entry.

    In the meantime please use the worker entrypoint to make it work.

  10. moved this from Waiting for: Product Owner to No status in GitHub Issues with 👀 3on Jul 2, 2026
  11. isaacs commented on Oct 14, 2026

    @isaacs
    Member

    I hit the same thing on @astrojs/cloudflare 13.7 while working on #24522, and filed #25344 before I found this one. I'd propose a broader fix than matching virtual:cloudflare/worker-entry, and then close one of the two issues.

    @sentry/cloudflare/vite will be able to auto-instrument a Worker whose wrangler main is a package export as of #24522. That will cover @astrojs/cloudflare/entrypoints/server, which the adapter uses as main when the wrangler config sets none. So @sentry/astro could add that plugin for Workers builds, in place of its own Cloudflare plugin:

    • The wrap no longer depends on the adapter's virtual module IDs, which have changed twice now.
    • Options come from an instrument.server.* file beside the wrangler config, or from env, not from () => undefined.
    • Users no longer need a hand-written sentry.server.config.* as main.

    Two more changes in @sentry/astro are needed for this to work. I found both when I tried this in the astro-6-cf-workers e2e app:

    1. Without a sentry.server.config.* file, the integration injects its default server init into SSR pages, with dsn: import.meta.env.PUBLIC_SENTRY_DSN. On Workers that builds as dsn: undefined, so it disables the transport. It should skip that snippet on Workers.
    2. The integration adds its own orchestrion plugin, and @sentry/cloudflare/vite adds another one (with the workerd-safe dcModule). Only the Cloudflare one should stay.

    Apps that keep sentry.server.config.* as main would still work, because the plugin leaves an entry that is already wrapped alone.

    #25344 has the details. If this sounds right, I can take it and close this issue as a duplicate, or close #25344 in favor of this one, whichever you prefer.

  12. added a commit that references this issue on Oct 15, 2026
    0778826
  13. github-actions commented on Oct 23, 2026

    @github-actions
    Contributor

    A PR closing this issue has just been released 🚀

    This issue was referenced by PR #25347, which was included in the 11.9.0 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions