Skip to content

Source maps on Vercel: why your production stack traces are unreadable

A minified trace means the build never uploaded source maps. The auth token, the release name and the preview environment are the three things that are usually wrong.

Sentry4 min readships at docs/solutions/sentry/source-maps-on-vercel.md

Tags: sentry · source-maps · vercel · deployment · releases

The error arrives. You open it, and the stack trace looks like this:

TypeError: Cannot read properties of undefined (reading 'id')
  at t (/_next/static/chunks/4823-a91e2f.js:1:48210)
  at n (/_next/static/chunks/4823-a91e2f.js:1:52117)
  at o (/_next/static/chunks/main-app-7b2c1d.js:1:9932)

Three single-letter functions and a column number in a one-line file. It is technically the truth and completely useless.

Sentry can un-minify this, but only if the build uploaded the source maps and tagged them with the same release identifier the running code reports. When one of those two halves is missing, you get exactly the output above, with no error, no warning, and a green build.

What has to line up

  1. next build generates source maps for client and server bundles.
  2. The build uploads them to Sentry, tagged with a release name.
  3. The running app reports the same release name in every event.
  4. The maps are deleted from the deployed output, so nobody can read your source by visiting /_next/static/chunks/4823-a91e2f.js.map.

withSentryConfig does 1, 2 and 4. Step 3 is on you, and it is the one that silently breaks.

The configuration

// next.config.ts
import { withSentryConfig } from "@sentry/nextjs/config"; // Sentry 11+
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  // your config
};

export default withSentryConfig(nextConfig, {
  org: process.env.SENTRY_ORG,
  project: process.env.SENTRY_PROJECT,
  authToken: process.env.SENTRY_AUTH_TOKEN,
  silent: !process.env.CI,
  tunnelRoute: "/monitoring",
  sourcemaps: { deleteSourcemapsAfterUpload: true },
});

And the release, set identically everywhere the SDK is initialised:

// sentry.server.config.ts and sentry.edge.config.ts
release: process.env.VERCEL_GIT_COMMIT_SHA,

// src/instrumentation-client.ts: note the NEXT_PUBLIC_ prefix
release: process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,

Vercel exposes both automatically; you do not set them yourself. The client one needs the NEXT_PUBLIC_ prefix because it has to be inlined into the browser bundle at build time.

The five things that are actually wrong

1. SENTRY_AUTH_TOKEN is not set in the build environment

The most common cause by a distance. The plugin cannot upload without a token, and it does not fail the build when the token is missing: it logs a line you did not read and carries on.

Set it in Vercel > Settings > Environment Variables, for Production and Preview. .env.local is not enough: it is not in the repository, so the Vercel build never sees it.

The token needs project:releases scope. Create it under Sentry Settings > Auth Tokens, and treat it as a write credential for your whole organisation, because that is what it is.

2. Preview deployments were forgotten

A very common shape: production traces are readable, preview traces are not. Environment variables in Vercel are scoped per environment, and someone ticked only Production. Every preview then builds without a token.

Tick Preview as well. If you use it, tick Development too.

3. The release names do not match

The build uploads maps under one name and the running app reports another, so Sentry has maps and events that never meet. Symptoms: the release exists in Sentry with artifacts attached, but issues show minified frames.

Check the issue's Tags > release and compare it with the release listed under Sentry's Releases page. They must be byte-identical. Two ways this breaks:

  • The client uses VERCEL_GIT_COMMIT_SHA (undefined in the browser) instead of NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA.
  • Someone hardcoded release: "1.0.0" in one config and left the others on the commit SHA.

4. Server-side traces are minified but client ones are fine

Next.js does not emit server source maps in production by default. The Sentry plugin turns them on for you; if you have an explicit productionBrowserSourceMaps or a custom webpack config that overrides devtool, you can end up with client maps only. Remove the override and let the plugin manage it.

5. The maps uploaded, but to the wrong project

SENTRY_ORG and SENTRY_PROJECT point at project A, the DSN points at project B. Everything succeeds and nothing lines up. Compare the DSN's project id with the project slug in your environment variables.

Verifying, without waiting for a real error

Make the build noisy first:

SENTRY_AUTH_TOKEN=... SENTRY_ORG=... SENTRY_PROJECT=... bun run build

With silent: false you should see the plugin resolve your org and project, create a release, and upload a number of artifacts. Zero artifacts means the upload did not happen; read the line above it.

Then check in Sentry: Releases > your commit SHA > Artifacts. If files are listed there and issues are still minified, the problem is release-name mismatch (cause 3), not upload.

Finally, deploy a throwaway route that throws, hit it, and confirm the issue shows a real file path and line number. Delete the route.

Do not commit the token

SENTRY_AUTH_TOKEN is a build-time secret with write access to your Sentry organisation. It must never appear in next.config.ts as a literal, never be prefixed NEXT_PUBLIC_, and never be read by application code. It belongs in Vercel's environment variables and in your local .env.local, which is gitignored. If it ever lands in a commit, revoke it in Sentry immediately: rotating is thirty seconds and there is no reason to hesitate.