Skip to content

[FEATURE] Emit a source map from the server bundle so deployed worker.js stack traces can be remapped #1352

Open

Description

@FriendlyCityJohnstown

Problem

The server bundling step calls esbuild without a sourcemap option, so .open-next/worker.js ships with no accompanying .map.

In v1.20.2, dist/cli/build/bundle-server.js sets bundle, outfile, format, target, minifyWhitespace, minifyIdentifiers, minifySyntax, legalComments, metafile, conditions and plugins — but nothing for sourcemap. grep -rni "sourcemap" across the whole installed package returns zero hits, and CloudflareOverrides / the cloudflare config block expose no escape hatch to enable it.

That severs the mapping chain for any error-reporting setup:

TS source -> next build (minified, .next/*.map produced)
          -> opennextjs-cloudflare esbuild   <-- no map emitted, chain breaks here
          -> wrangler esbuild -> deployed worker.js

Because nothing maps .open-next/worker.js positions back to .next/server/*, downstream tooling cannot recover original frames no matter how it is configured:

  • Sentry — withSentryConfig uploads maps for the Next build only. Runtime frames arrive as bare offsets (worker.js:49444:191 (as)). The usual sentry-cli sourcemaps inject + wrangler deploy --no-bundle recipe does not help, because the map it injects describes the wrangler pass only.
  • Cloudflare's own upload_source_maps: true — produces a map for the final wrangler bundle whose sources resolve to the already-bundled OpenNext output, not to app source.

Possibly the underlying cause of the downstream report in getsentry/sentry-javascript#19213 (Next.js + OpenNext + Workers: source maps upload with valid Debug IDs, yet runtime frames still fail to resolve; currently labelled State: Blocked / External Dependency).

Request

Emit a source map from the server bundling pass — ideally opt-in via a build flag or an open-next.config.ts option, chaining from the Next build's own maps where available so the result maps back to original sources rather than to .next/server/* intermediates.

Even an unchained map (final bundle → .next/server/*) would be a meaningful improvement, since it could then be composed with the maps Next already produces.

Environment

  • @opennextjs/cloudflare 1.20.2
  • next 16.2.12
  • @sentry/nextjs 10.63.0

Happy to open a PR if you'd like this behind a flag — just let me know your preference on opt-in vs. default and on chaining.

Activity

  1. FriendlyCityJohnstown commented on Sep 14, 2026

    @FriendlyCityJohnstown
    Author

    Measured cost of the OPEN_NEXT_DEBUG workaround — and why it is not a build-only switch

    Sharing a measurement in case it helps prioritise this. Next 16.3.4, @opennextjs/cloudflare 1.20.6, @opennextjs/aws 4.1.4. Two builds of the same commit, same machine, back to back — one default, one with OPEN_NEXT_DEBUG=true:

    default OPEN_NEXT_DEBUG=true
    worker.js 20,532,095 B 21,683,373 B (+5.6%)
    worker.js.map 29,036,508 B 33,609,592 B (+15.7%)
    sources in the map 100 712
    application lib/** sources in the map 1 6, plus many .next/server/**
    wrangler deploy --dry-run 20050.87 KiB / gzip 4037.13 KiB 21175.23 KiB / gzip 4117.75 KiB

    It works — the composed map starts carrying real application modules instead of only .open-next/**, unenv and node polyfills. The shipped-size cost is also small: +2.0% gzip, because the minify coupling inflates the intermediate .open-next artifacts while the bundle that actually ships (wrangler's own esbuild pass) grows only 5.6% uncompressed.

    The problem is that the flag is not build-only. globalThis.openNextDebug is read at runtime:

    site runtime effect
    adapters/logger.js:3 every OpenNext debug(...) becomes a real console.log
    core/routing/util.js:253-258 adds X-OpenNext-Version and X-OpenNext-RequestId to responses
    adapters/middleware.js:79 adds the internal request-id header

    So on Cloudflare Workers, getting a sourcemap for the server bundle currently also means switching production into debug logging and two extra response headers.

    The ask: let minify and sourcemap be set independently of debug. In @opennextjs/aws, build/helper.js binds all three together:

    helper.js:24    const debug = Boolean(process.env.OPEN_NEXT_DEBUG) ?? false;
    helper.js:34    minify: !debug,
    helper.js:91    sourcemap: debug ? "inline" : false,
    helper.js:122   sourcemap: debug ? "inline" : false,

    (That binding comes from #629, "feat(debug): never minify in debug mode".) A dedicated env var — or simply honouring an explicit sourcemap/minify option — would let a minified bundle ship with a map attached and no debug side effects. Emitting the map as a sibling .map rather than inline would additionally avoid embedding anything in the uploaded script.

    Related: #824 (specifically handler.mjs), #1124.

    I have a reproducible local setup for the byte-for-byte comparison above and am happy to re-run it against a branch if that would help.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions