nextjs - ✅(Solved) Fix loadEnvConfig silently ignores directory argument in monorepo due to internal cache [4 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#92040Fetched 2026-04-08 01:45:01
View on GitHub
Comments
0
Participants
1
Timeline
6
Reactions
2
Participants
Timeline (top)
cross-referenced ×4issue_type_added ×1referenced ×1

Error Message

This completely ignores the dir argument — so when user code calls loadEnvConfig with a different directory (the monorepo root), it hits the cache and returns the previous empty result with no warning or error. This is a common monorepo pattern where a single .env lives at the workspace root rather than inside each app. The issue is completely silent — no error, no warning — making it very hard to debug. Users typically waste time verifying paths, file permissions, and variable names before discovering the forceReload workaround by reading the source code directly.

Root Cause

Root cause: Next.js calls loadEnvConfig internally before any user code runs, caching the result in a module-level variable. The cache check is:

Fix Action

Fix / Workaround

Workaround: Pass forceReload: true as the 4th positional argument:

loadEnvConfig(
  path.resolve(process.cwd(), '../../'),
  undefined,
  undefined,
  true // forceReload
)

This is a common monorepo pattern where a single .env lives at the workspace root rather than inside each app. The issue is completely silent — no error, no warning — making it very hard to debug. Users typically waste time verifying paths, file permissions, and variable names before discovering the forceReload workaround by reading the source code directly.

PR fix notes

PR #92047: fix(@next/env): respect loadEnvConfig cache per directory

Description (problem / solution / changelog)

Fixes #92040.

Summary

  • reuse the cached @next/env result only when loadEnvConfig is called for the same resolved directory
  • reset the previous loaded-file list when switching directories so reload bookkeeping does not leak across roots
  • add a regression that loads two app roots back to back without orceReload

Testing

  • git diff --check
  • manual ode --experimental-strip-types harness against packages/next-env/index.ts covering process-env identity and two-directory reload behavior

Changed files

  • packages/next-env/index.ts (modified, +7/-2)
  • test/unit/preserve-process-env.test.ts (modified, +39/-1)

PR #92054: fix(@next/env): include dir in loadEnvConfig cache key

Description (problem / solution / changelog)

What?

Include dir as part of the loadEnvConfig cache key in @next/env.

Why?

loadEnvConfig caches its result in module-level state, but the cache check only tested whether any load had happened — it never compared the dir argument:

// Before (broken)
if (combinedEnv && !forceReload) {
  return { combinedEnv, parsedEnv, loadedEnvFiles: cachedLoadedEnvFiles }
}

In a monorepo where .env lives at the workspace root, Next.js internally calls loadEnvConfig with the app directory first (no .env there, so loadedEnvFiles: []). When user code then calls it with the monorepo root — a common pattern to load shared env vars — it silently hits the stale cache and returns empty results. DATABASE_URL (and all other vars) are undefined with no error and no warning.

The only workaround was the undocumented forceReload: true 4th argument, which also requires passing undefined twice to reach it.

Reproduction: https://github.com/aynaitlamine/loadenvconfig-nextjs-issue-reproduction

How?

Added a module-level cachedDir variable. The cache is now only returned when cachedDir === dir, and cachedDir is updated whenever a fresh load runs. The forceReload path is unchanged.

// After (fixed)
if (combinedEnv && !forceReload && cachedDir === dir) {
  return { combinedEnv, parsedEnv, loadedEnvFiles: cachedLoadedEnvFiles }
}
  • Related issues linked using fixes #92040
  • Tests added: test/unit/load-env-config-dir-cache.test.ts

fixes #92040

Changed files

  • packages/next-env/index.ts (modified, +4/-2)
  • test/unit/load-env-config-dir-cache.test.ts (added, +56/-0)

PR #92061: fix(@next/env): include dir in cache key for loadEnvConfig

Description (problem / solution / changelog)

Problem

loadEnvConfig in @next/env uses module-level variables to cache the result. The cache guard:

if (combinedEnv && !forceReload) {
  return { combinedEnv, parsedEnv, loadedEnvFiles: cachedLoadedEnvFiles }
}

ignores the dir argument — so when Next.js internally calls loadEnvConfig (with the app directory) before user code runs, then user code calls loadEnvConfig with a different directory (e.g. a monorepo root), the cache returns the previous result for the wrong directory with no warning or error.

This is especially common in monorepo setups where .env files live in a parent directory outside the Next.js app.

Fixes #92040

Fix

Add a cachedDir module-level variable and include it in the cache check:

if (combinedEnv && !forceReload && cachedDir === dir) {
  return { combinedEnv, parsedEnv, loadedEnvFiles: cachedLoadedEnvFiles }
}

cachedDir is updated whenever a fresh load is performed. The existing forceReload: true workaround continues to work.

Testing

Added test/unit/load-env-cache-dir.test.ts covering:

  • Distinct directories load their respective .env files
  • Different dir without forceReload is no longer silently cached from previous dir
  • Same dir without forceReload still correctly uses the cache

Changed files

  • packages/next-env/index.ts (modified, +3/-1)
  • test/unit/load-env-cache-dir.test.ts (added, +52/-0)

PR #13: dockerised the app

Description (problem / solution / changelog)

Task #11 is completed creating PR

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

Summary by CodeRabbit

  • New Features

    • Short-link creation for authenticated users with persistent storage.
    • PostgreSQL-backed data storage with pgAdmin management.
  • Documentation

    • End-to-end guide for deploying to Google Cloud with CI/CD integration.
  • Chores

    • Added containerization and multi-container orchestration for local and production runs.
    • Configured Next.js for standalone production builds and added runtime support for Google tooling and Postgres.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Changed files

  • .dockerignore (added, +15/-0)
  • .github/instructions/gcp-deployment-intructions.md (added, +87/-0)
  • Dockerfile (added, +40/-0)
  • GEMINI.md (added, +0/-0)
  • actions.ts (added, +25/-0)
  • docker-compose.yml (added, +45/-0)
  • next.config.mjs (added, +6/-0)
  • package-lock.json (modified, +574/-0)
  • package.json (modified, +2/-0)
  • repositories.ts (added, +23/-0)
  • services.ts (added, +40/-0)

Code Example

/my-monorepo
  .envDATABASE_URL=postgres://...
  apps/
    web/Next.js app
      envConfig.ts
      next.config.ts

---

import { loadEnvConfig } from '@next/env'
import path from 'node:path'

const { loadedEnvFiles, combinedEnv } = loadEnvConfig(
  path.resolve(process.cwd(), '../../') // monorepo root
)

console.log(loadedEnvFiles)           // []
console.log(combinedEnv.DATABASE_URL) // undefined

---

import './envConfig.ts'
import type { NextConfig } from 'next'

console.log(process.env.DATABASE_URL) // undefined

---

if (combinedEnv && !forceReload) {
  return { combinedEnv, parsedEnv, loadedEnvFiles: cachedLoadedEnvFiles }
}

---

loadEnvConfig(
  path.resolve(process.cwd(), '../../'),
  undefined,
  undefined,
  true // forceReload
)

---

// Instead of:
if (combinedEnv && !forceReload) return cached

// Should be:
if (combinedEnv && !forceReload && cachedDir === dir) return cached

---

Operating System:
  Platform: linux
  Arch: x64
  Version: #14~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Jan 15 15:52:10 UTC 2
  Available memory (MB): 7816
  Available CPU cores: 4
Binaries:
  Node: 24.12.0
  npm: 11.6.2
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 16.2.1 // Latest available version is detected (16.2.1).
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/aynaitlamine/loadenvconfig-nextjs-issue-reproduction

To Reproduce

  1. Create a monorepo with this structure:
/my-monorepo
  .env               ← DATABASE_URL=postgres://...
  apps/
    web/             ← Next.js app
      envConfig.ts
      next.config.ts
  1. In apps/web/envConfig.ts:
import { loadEnvConfig } from '@next/env'
import path from 'node:path'

const { loadedEnvFiles, combinedEnv } = loadEnvConfig(
  path.resolve(process.cwd(), '../../') // monorepo root
)

console.log(loadedEnvFiles)           // []
console.log(combinedEnv.DATABASE_URL) // undefined
  1. Import it as the first import in next.config.ts:
import './envConfig.ts'
import type { NextConfig } from 'next'

console.log(process.env.DATABASE_URL) // undefined
  1. Run next devDATABASE_URL is always undefined despite the .env file existing at the resolved path.

Current vs. Expected behavior

Current: loadEnvConfig silently returns loadedEnvFiles: [] and undefined for all env vars, even when the directory and .env file are both valid and exist.

Expected: loadEnvConfig should load the .env file from the provided directory and populate process.env accordingly.

Root cause: Next.js calls loadEnvConfig internally before any user code runs, caching the result in a module-level variable. The cache check is:

if (combinedEnv && !forceReload) {
  return { combinedEnv, parsedEnv, loadedEnvFiles: cachedLoadedEnvFiles }
}

This completely ignores the dir argument — so when user code calls loadEnvConfig with a different directory (the monorepo root), it hits the cache and returns the previous empty result with no warning or error.

Workaround: Pass forceReload: true as the 4th positional argument:

loadEnvConfig(
  path.resolve(process.cwd(), '../../'),
  undefined,
  undefined,
  true // forceReload
)

This is undocumented, unintuitive, and requires passing undefined for two arguments just to reach forceReload.

The fix should be to include dir in the cache key:

// Instead of:
if (combinedEnv && !forceReload) return cached

// Should be:
if (combinedEnv && !forceReload && cachedDir === dir) return cached

Or at minimum, log a warning when the cache is returned for a different directory than originally cached.


Which area(s) are affected: @next/env, Monorepo support

Which stage(s) are affected: next dev, next build


Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #14~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Jan 15 15:52:10 UTC 2
  Available memory (MB): 7816
  Available CPU cores: 4
Binaries:
  Node: 24.12.0
  npm: 11.6.2
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 16.2.1 // Latest available version is detected (16.2.1).
  eslint-config-next: N/A
  react: 19.2.4
  react-dom: 19.2.4
  typescript: 5.9.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Not sure

Which stage(s) are affected? (Select all that apply)

next dev (local)

Additional context

This is a common monorepo pattern where a single .env lives at the workspace root rather than inside each app. The issue is completely silent — no error, no warning — making it very hard to debug. Users typically waste time verifying paths, file permissions, and variable names before discovering the forceReload workaround by reading the source code directly.

extent analysis

Fix Plan

To fix the issue with loadEnvConfig not loading the .env file from the provided directory, you can modify the loadEnvConfig function to include the dir in the cache key. However, since this is a part of the @next/env package, it's more practical to use the forceReload option as a workaround.

Here are the steps to fix the issue:

  • Pass forceReload: true as the 4th positional argument to loadEnvConfig:
const { loadedEnvFiles, combinedEnv }!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! = loadEnvConfig(
  path.resolve(process.cwd(), '../../'), // monorepo root
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  undefined,
!  undefined,
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! true // forceReload
)

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Alternatively, you can create a custom function to load!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING