-
Notifications
You must be signed in to change notification settings - Fork 30.7k
loadEnvConfig silently ignores directory argument in monorepo due to internal cache #92040
Description
Link to the code that reproduces this issue
https://github.com/aynaitlamine/loadenvconfig-nextjs-issue-reproduction
To Reproduce
- Create a monorepo with this structure:
/my-monorepo
.env ← DATABASE_URL=postgres://...
apps/
web/ ← Next.js app
envConfig.ts
next.config.ts
- 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- 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- Run
next dev—DATABASE_URLis alwaysundefineddespite the.envfile 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 cachedOr 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/AWhich 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.