How to Fix: Support node –conditions/-C as a `NODE_OPTION`.
Why -C=development Fails Inside NODE_OPTIONS and How to Fix It
The error is not coming from Next.js. It happens earlier, inside Node.js option parsing: --conditions (or -C) is accepted on the command line, but rejected when passed through NODE_OPTIONS. That mismatch breaks workflows that rely on conditional exports in local development, CI, and framework wrappers.
Understanding the Root Cause
NODE_OPTIONS is a special environment variable that allows selected Node.js CLI flags to be injected into a process before application code runs. Node does not allow every CLI flag inside NODE_OPTIONS. Instead, it maintains an internal allowlist for security, stability, and startup predictability.
The issue here is that --conditions and its short alias -C work when passed directly to node, but they were not recognized as valid entries in the NODE_OPTIONS allowlist. As a result, startup fails with an error similar to:
-C=development is not allowed in NODE_OPTIONS
Technically, –conditions influences how Node resolves packages that use conditional exports in package.json. For example, a package may expose different entry points for development, production, node, or custom conditions. When you want a framework like Next.js to run under a custom condition, using NODE_OPTIONS is often the most practical mechanism because it propagates into child processes and wrapper scripts automatically.
So the bug is not that --conditions is invalid. The bug is that Node supports the flag on the CLI but not via NODE_OPTIONS, which creates inconsistent behavior.
Step-by-Step Solution
The correct fix in Node.js is to add --conditions and its short form -C to the list of options allowed in NODE_OPTIONS.
1. Reproduce the problem
You can verify the issue with a minimal command:
NODE_OPTIONS='-C=development' node -e "console.log('started')"
Expected behavior:
started
Actual behavior on affected Node versions:
-C=development is not allowed in NODE_OPTIONS
The long form fails the same way:
NODE_OPTIONS='--conditions=development' node -e "console.log('started')"
2. Confirm that the CLI flag itself works
Run the exact same condition directly on the Node command line:
node --conditions=development -e "console.log('started')"
If that works, you have confirmed the inconsistency is specifically in NODE_OPTIONS parsing.
3. Apply the Node.js code fix
Inside the Node.js source, locate the code that defines which flags are supported by NODE_OPTIONS. The fix is to register --conditions and -C as permitted environment options in the same way other safe runtime flags are registered.
The exact location may vary by Node version, but the implementation typically lives in the option parsing setup under Node’s source tree. You are looking for the section where options are declared with metadata indicating whether they are allowed in NODE_OPTIONS.
The change conceptually looks like this:
// Pseudocode: register conditions as allowed in NODE_OPTIONS
AddOption("--conditions", {
type: "string",
env_settings: kAllowedInEnvvar
});
AddAlias("-C", "--conditions");
If the alias is defined separately from the main option, make sure both forms are covered consistently.
4. Add regression tests
This bug should be protected with a test that validates startup succeeds when NODE_OPTIONS includes --conditions or -C.
// Pseudocode test case
spawn(process.execPath, ['-e', 'console.log("ok")'], {
env: {
...process.env,
NODE_OPTIONS: '--conditions=development'
}
})
// Expect exit code 0 and stdout to contain "ok"
Add a second case for the short flag:
// Pseudocode
NODE_OPTIONS='-C=development'
Good regression tests should check:
- Process starts successfully
- No validation error is thrown for
NODE_OPTIONS - The condition is actually applied during module resolution if a fixture package with conditional exports is used
5. Validate with a real package export condition
To prove the fix works end-to-end, create a package fixture that exposes different files depending on a custom condition:
{
"name": "demo-pkg",
"exports": {
".": {
"development": "./dev.js",
"default": "./default.js"
}
}
}
Then load it with and without the environment variable:
NODE_OPTIONS='--conditions=development' node app.js
// app.js
console.log(require('demo-pkg'))
After the fix, Node should resolve the development branch instead of the default one.
6. Temporary workaround if you cannot patch Node yet
If you are blocked on an unpatched Node release, use the flag directly in the startup command instead of NODE_OPTIONS:
node --conditions=development node_modules/next/dist/bin/next dev
Or via package scripts:
{
"scripts": {
"dev": "node --conditions=development ./node_modules/next/dist/bin/next dev"
}
}
This workaround is less flexible because some toolchains and nested process launches depend on NODE_OPTIONS propagation, but it avoids the immediate startup failure.
Common Edge Cases
1. Short alias vs long option
Some implementations register the primary option but forget the alias. If --conditions=development works and -C=development still fails, the short form likely was not mapped correctly in the environment option parser.
2. Multiple conditions
Node supports multiple active conditions in some invocation patterns. Verify parsing behavior for repeated flags or space-separated environment values:
NODE_OPTIONS='--conditions=development --conditions=custom'
If only the first condition is honored, check whether the option parser stores a single string instead of an array or repeated value set.
3. Shell quoting differences
NODE_OPTIONS is shell-sensitive. Commands that work in Bash may need different quoting in PowerShell or Windows CMD.
# Bash / zsh
NODE_OPTIONS='--conditions=development' node app.js
# PowerShell
$env:NODE_OPTIONS='--conditions=development'; node app.js
# CMD
set NODE_OPTIONS=--conditions=development && node app.js
4. Conditional exports not defined in the package
Even after the parser fix, nothing changes unless the target package actually uses exports conditions in its package.json. If there is no matching branch for development, Node falls back to other conditions such as default.
5. Framework wrappers spawning child processes
One reason this bug is impactful is that frameworks like Next.js may spawn subprocesses internally. Direct CLI flags do not always flow into those child processes, while NODE_OPTIONS usually does. If behavior still looks inconsistent after the fix, inspect whether the framework sanitizes environment variables before spawning.
6. Version mismatch
If your local build works but CI still fails, check the exact Node.js version in each environment. The fix must exist in the runtime actually executing the process.
FAQ
Why does node --conditions=development work, but NODE_OPTIONS='--conditions=development' fail?
Because Node treats CLI options and NODE_OPTIONS differently. Only a predefined set of flags is allowed through the environment variable. On affected versions, --conditions is supported on the CLI but missing from that allowlist.
Is this a Next.js bug?
No. Next.js only exposes the issue because it runs on top of Node.js. The rejection happens before framework code executes, during Node process startup.
What is the safest workaround until Node includes the fix?
Invoke Node directly with --conditions in your script or command line instead of using NODE_OPTIONS. That avoids the allowlist restriction, though it may not propagate to every child process.
The core takeaway is simple: supporting --conditions as a CLI flag is not enough. For real-world framework and tooling compatibility, Node must also allow --conditions and -C inside NODE_OPTIONS. Once that allowlist entry is added and covered by regression tests, the inconsistency disappears and conditional exports become usable in the environments where developers actually need them.