payloadcms / payloadcms/payload

loadEnv fails with "Cannot destructure property 'loadEnvConfig'" on Next.js 15.5+

Open Beginner friendly
#16,674 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

stale
Dominant language
TypeScript
Stars
44.8k
Forks
4.2k
Avg merge
2d 21h
Merged PRs (30d)
53

Description

loadEnv fails with Cannot destructure property 'loadEnvConfig' of 'import_env.default' as it is undefined on Next.js 15.5+

Summary

payload/dist/bin/loadEnv.js does import nextEnvImport from '@next/env' and then destructures loadEnvConfig from it. On Next.js 15.5+ the @next/env package no longer exposes a default export — only named exports remain — so nextEnvImport is undefined when the module is loaded through certain interop paths, and the destructure throws.

This affects any tooling that imports payload/node (which re-exports loadEnv from bin/loadEnv.js), including running custom scripts that import payload.config via tsx.

Reproduction

  • payload@3.84.1
  • @payloadcms/db-vercel-postgres@3.84.1 (which transitively imports payload/node via @payloadcms/drizzle/utilities/blocksToJsonMigrator)
  • next@15.5.18 (pins @next/env@15.5.18)
  • tsx@4.21.0
  • Node.js v22.14

A script that does:

import { getPayload } from "payload";
import config from "../payload.config";
const payload = await getPayload({ config });

run via yarn dotenv -e .env.local -- tsx scripts/foo.ts produces:

TypeError: Cannot destructure property 'loadEnvConfig' of 'import_env.default' as it is undefined.
    at loadedEnvFiles (node_modules/payload/dist/bin/loadEnv.js:3:9)

Root cause

node_modules/@next/env/dist/index.js is a webpack-bundled CJS file whose module.exports is { initialEnv, updateInitialEnv, processEnv, resetEnv, loadEnvConfig } — there is no default property.

Verification:

node -e "console.log(require('@next/env').default)"
# => undefined
node -e "import('@next/env').then(m => console.log(typeof m.default, Object.keys(m)))"
# => object [ initialEnv, updateInitialEnv, processEnv, resetEnv, loadEnvConfig ]

Older Next.js (pre-15.5) exposed module.exports.default = ... for legacy default-import compatibility, so the existing Payload code happened to work. PR #7047 fixed an earlier instance of this on a different path, but the same anti-pattern remains in bin/loadEnv.js.

Proposed fix

-import nextEnvImport from '@next/env';
+import * as nextEnvImport from '@next/env';
 import { findUpSync } from '../utilities/findUp.js';
-const { loadEnvConfig } = nextEnvImport;
+const { loadEnvConfig } = nextEnvImport.default ?? nextEnvImport;

The ?? nextEnvImport fallback keeps backwards-compat with Next.js < 15.5 (where default is the namespace object), while supporting 15.5+ (where named exports live on the namespace itself).

I have this applied locally via patch-package and it resolves the error.

Environment

Package Version
payload 3.84.1
@payloadcms/next 3.84.1
@payloadcms/db-vercel-postgres 3.84.1
next 15.5.18
@next/env 15.5.18
tsx 4.21.0
Node.js 22.14.0

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the failure with payload 3.84.1, Next.js 15.5.18, and the listed tsx script, then inspect payload/dist/bin/loadEnv.js and its source counterpart. Verify how @next/env is imported across the supported Next.js versions. Done means importing payload/node no longer throws and loadEnvConfig remains compatible with older and newer @next/env exports.

Written by the indexing model from the issue text.

Assessment

Tech stack
next.js, typescript
Domain
backend, tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.