How to Fix: Source map error when accessing .env variables

5 min read

The crash is triggered by a server/client boundary mismatch: a route that gets rendered in the browser ends up pulling in code that reads .env values on the wrong side, and during reload Next.js tries to resolve source maps for a module path it cannot safely expose. The result looks like a source map failure, but the real problem is how environment variables are being accessed inside the App Router rendering flow.

Understanding the Root Cause

In Next.js, environment variables are split into two categories:

  • Server-only variables: values like process.env.MY_SECRET that should never reach the browser.
  • Client-exposed variables: values prefixed with NEXT_PUBLIC_, which Next.js can safely inline into client bundles.

This issue appears when a component, helper, or imported module used by a browser-rendered page reads a non-public .env variable. On the first pass, development mode may appear to tolerate it, but after reload the bundler and source-map pipeline re-evaluate the module graph. Once Next.js detects that a client-reachable module depends on server-only environment access, the dev server can surface a confusing source map error instead of a clearer configuration error.

Why does it happen specifically on reload? Because the development compiler, React Server Components graph, and browser refresh path do extra reconciliation work. If a page like /page1 imports a shared module that references process.env.SOME_VAR, Next.js may attempt to map the transformed output back to the original source during refresh. That breaks when the module belongs to a boundary where the variable cannot be serialized or bundled for the client.

In short, the bug is not really that source maps are broken. The real root cause is:

  • a client-accessible module reads a server-only env variable, or
  • a shared module is imported by both server and client code, but contains process.env access that should only exist on the server.

Step-by-Step Solution

The fix is to make the env usage match the runtime boundary. Use one of the following approaches depending on what the variable is meant for.

1. Identify where the env variable is being read

Search for direct access like this:

process.env.MY_VAR

Also inspect shared utilities imported into pages, layouts, hooks, or client components.

2. If the value is needed in the browser, rename it with NEXT_PUBLIC_

Update your .env.local:

NEXT_PUBLIC_API_BASE_URL=https://example.com

Then read it in client-safe code:

export const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;

This is the correct fix if the value is intentionally public, such as a public API origin, analytics key, or feature flag that is safe to expose.

3. If the value is secret, keep it on the server

Do not read secret env variables inside client components or shared browser code.

Bad:

'use client';

export default function Page() {
  const secret = process.env.MY_SECRET;
  return <div>{secret}</div>;
}

Good: move the env access into a server component, route handler, or server-only utility.

export default async function Page() {
  const secret = process.env.MY_SECRET;
  return <div>Server rendered successfully</div>;
}

If the browser needs data derived from that secret, fetch it from an API route or server action instead of exposing the secret directly.

4. Split shared utilities into server-only and client-safe files

A common cause is a utility file that mixes env access with generic helper logic.

Problematic shared utility:

export const apiKey = process.env.MY_SECRET;

export function formatName(name: string) {
  return name.trim().toUpperCase();
}

Safer split:

// lib/server/env.ts
export const apiKey = process.env.MY_SECRET;
// lib/shared/format.ts
export function formatName(name: string) {
  return name.trim().toUpperCase();
}

Then import lib/server/env.ts only from server code.

5. Explicitly mark server-only modules

If you are on a modern Next.js setup, add a server-only guard to prevent accidental client imports.

import 'server-only';

export const secret = process.env.MY_SECRET;

This gives a much clearer failure mode than a misleading source map error.

6. Restart the dev server after changing env variables

After renaming or moving variables, stop and restart development:

npm run dev

Next.js reads env configuration at startup, so a simple browser reload is often not enough.

Use this pattern to keep boundaries clean:

// app/page1/page.tsx
export default async function Page1() {
  const data = await getServerData();
  return <div>{data.message}</div>;
}
// lib/server/data.ts
import 'server-only';

export async function getServerData() {
  const token = process.env.MY_SECRET;

  return {
    message: token ? 'Loaded safely on the server' : 'Missing env'
  };
}

If some value must be used in a client component, pass it down as props only if it is safe to expose, or use a NEXT_PUBLIC_ variable.

Common Edge Cases

Using a client component indirectly

Even if the env access is not inside a file with 'use client', the module can still become client-reachable if it is imported by a client component somewhere deeper in the tree.

Destructuring process.env

This pattern can behave unexpectedly in bundling and should be avoided:

const { MY_SECRET } = process.env;

Prefer direct property access:

const secret = process.env.MY_SECRET;

Expecting runtime updates without restart

Editing .env.local during development does not always hot-reload the way component code does. Restart the server after changes.

Using public prefixes for secrets by mistake

Adding NEXT_PUBLIC_ fixes the error only if the value is safe for the browser. Never use it for database credentials, private API keys, or signing secrets.

Importing server helpers into hooks

Custom React hooks often run in client components. If a hook imports a helper that reads process.env.MY_SECRET, the same issue can reappear.

FAQ

Why does Next.js show a source map error instead of an env error?

Because the failure often surfaces during the development bundling and refresh pipeline. The underlying problem is an invalid server/client dependency, but the visible symptom can be emitted by the source-map stage.

Can I fix this by disabling source maps?

No. That only hides the symptom. The real fix is to move secret env access to server-only code or rename safe variables with NEXT_PUBLIC_.

What is the safest rule to follow?

Treat every process.env.* value as server-only by default. Only expose values with NEXT_PUBLIC_ when you explicitly want them available in the browser.

The practical resolution for this GitHub issue is simple: audit the module used by /page1, move any secret .env access into server-only files, expose only public values with NEXT_PUBLIC_, then restart the Next.js dev server. Once the runtime boundary is correct, the reload error disappears along with the misleading source map failure.

Leave a Reply

Your email address will not be published. Required fields are marked *