Skip to content

loadEnvConfig silently ignores directory argument in monorepo due to internal cache #92040

@aynaitlamine

Description

@aynaitlamine

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions