Exemplo
dotenv 17.4.2: Existing process.env variables and earlier loaded config files take strict precedence unless override: true or DOTENV_CONFIG_OVERRIDE is explicitly enabled
Amostra verificada para npm dotenv 17.4.2: Existing process.env variables and earlier loaded config files take strict precedence unless override: true or…
sha256:cc2a38a595028c0eb69e9cc8c1b0ea4df797c07aa2d8bfd5af537ec850ba4895
Esta rede oferece uma coisa: uma amostra que compila. Ela a executou em um sandbox e guardou o recibo assinado. Não classifica nem garante nada — se o mesmo código compila onde você está, ela não mediu.
Quantas chaves de assinatura distintas enviaram um recibo de contrato aprovado. Uma é só o autor; mais de uma significa que outra pessoa também o compilou. Uma chave é gerada por conta própria e não tem identidade registrada por trás, então conta chaves, não pessoas.
MIT-0
Evidência de execução
O ambiente declarado e as execuções assinadas ficam separados, para você ver exatamente o que esta amostra executou e onde.
- Base da evidência
- Contrato assinado aprovado
- Recibos de verificação
- 2
- Chaves de assinatura que o compilaram
- 2
Ambiente declarado
node linux x64 node node npm
Ambientes das execuções de verificação
| Ambiente | Contrato | Etapas | Execução |
|---|---|---|---|
| node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-16 |
| node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-18 |
Caso
HOW- Objetivo
- Existing process.env variables and earlier loaded config files take strict precedence unless override: true or DOTENV_CONFIG_OVERRIDE is explicitly enabled
- Pacotes
- Símbolos
-
- dotenv.config
- dotenv/config
- dotenv.populate
- Ambiente
- node
- Criado
- 2026-08-16T07:07:18Z
Contrato
- preset process.env variables are preserved when dotenv.config() runs with default override: false
- dotenv.config() returns parsed dictionary from file while process.env retains preexisting value
- subsequent dotenv.config() calls silently ignore overlapping keys without override: true
- passing override: true overwrites preexisting process.env values with values from file
- preloading via dotenv/config respects DOTENV_CONFIG_OVERRIDE=true to overwrite shell environment
Arquivos
- NOTES.md
- csx.json
- package-lock.json
- package.json
- src/index.mjs
- test/contract.mjs
Código-fonte
# Notes on dotenv override configuration trap
### search_known_solution Result
`search_known_solution` returned existing sample `sha256:51dd5f35fb9c7be3bc11ebc83f2c01779e197c88f316ecd1ff3173bacbe66884` ("process.env is undefined in an ESM module even though dotenv.config() ran and .env exists", covering ESM static import evaluation order).
### What a model would have written instead
A model would write sequential `dotenv.config({ path: '.env' })` followed by `dotenv.config({ path: '.env.local' })` (or expect `.env` to overwrite empty/preset container environment variables) expecting later files or `.env` values to overwrite `process.env` by default without passing `{ override: true }`.
### How the wrong version fails
Silently with a green build — `dotenv.config()` succeeds without error, `result.error` is `undefined`, and `result.parsed` shows the new values from disk, but `process.env` silently retains the preexisting/empty values because `override` defaults to `false`.
{"case":{"caseId":"case:sha256:9f7167990b320cecb53c269f7a61ed89ed0b0bc74caeb5f4bf498730c1637917","contract":["preset process.env variables are preserved when dotenv.config() runs with default override: false","dotenv.config() returns parsed dictionary from file while process.env retains preexisting value","subsequent dotenv.config() calls silently ignore overlapping keys without override: true","passing override: true overwrites preexisting process.env values with values from file","preloading via dotenv/config respects DOTENV_CONFIG_OVERRIDE=true to overwrite shell environment"],"goal":"Existing process.env variables and earlier loaded config files take strict precedence unless override: true or DOTENV_CONFIG_OVERRIDE is explicitly enabled","kind":"HOW","packages":["pkg:npm/dotenv@17.4.2"],"schemaVersion":1,"symbols":["dotenv.config","dotenv/config","dotenv.populate"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/dotenv@17.4.2"],"schemaVersion":1,"symbols":["dotenv.config","dotenv/config","dotenv.populate"],"verifierAdapter":"node-typescript@1"}
{
"name": "csx-sample-dotenv-override-config",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "csx-sample-dotenv-override-config",
"version": "1.0.0",
"license": "MIT-0",
"dependencies": {
"dotenv": "17.4.2"
}
},
"node_modules/dotenv": {
"version": "17.4.2",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz",
"integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
}
}
}
{
"name": "csx-sample-dotenv-override-config",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT-0",
"dependencies": {
"dotenv": "17.4.2"
}
}
import dotenv from 'dotenv';
/**
* Loads a .env configuration file into process.env.
* By default (override: false), existing process.env variables are preserved.
* Pass override: true to allow file variables to overwrite existing ones.
*
* @param {import('dotenv').DotenvConfigOptions} [options]
* @returns {import('dotenv').DotenvConfigOutput}
*/
export function loadEnv(options = {}) {
return dotenv.config({ quiet: true, ...options });
}
/**
* Loads layered .env files sequentially (e.g. ['.env', '.env.local']).
* If override is false (default), keys defined in earlier files cannot be overwritten by later files.
* If override is true, keys defined in later files overwrite earlier keys.
*
* @param {string[]} paths - Array of file paths to load in order
* @param {boolean} [override=false] - Whether subsequent files overwrite existing process.env keys
* @returns {Array<import('dotenv').DotenvConfigOutput>}
*/
export function loadLayeredEnv(paths, override = false) {
const results = [];
for (const envPath of paths) {
const res = dotenv.config({ path: envPath, override, quiet: true });
results.push(res);
}
return results;
}
export { dotenv };
import { strict as assert } from 'node:assert';
import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { loadEnv, loadLayeredEnv } from '../src/index.mjs';
const root = fileURLToPath(new URL('..', import.meta.url));
const envMainPath = path.join(root, '.env.contract-main');
const envBasePath = path.join(root, '.env.contract-base');
const envLocalPath = path.join(root, '.env.contract-local');
// 1. Setup temporary fixture files with permitted example hostnames
fs.writeFileSync(
envMainPath,
'API_ENDPOINT=https://api.example.org/v2\nEMPTY_TOKEN=super-secret-token\nNEW_KEY=fresh-value\n',
);
fs.writeFileSync(
envBasePath,
'DB_HOST=db.example.test\nDB_PORT=5432\nBASE_ONLY=base-config\n',
);
fs.writeFileSync(
envLocalPath,
'DB_HOST=localhost\nLOCAL_ONLY=local-config\n',
);
try {
// -------------------------------------------------------------------------
// Test 1: Existing process.env variables take strict precedence by default (override: false)
// -------------------------------------------------------------------------
process.env.API_ENDPOINT = 'https://api.example.com/v1';
process.env.EMPTY_TOKEN = '';
delete process.env.NEW_KEY;
const resDefault = loadEnv({ path: envMainPath });
// Parsing succeeds without error
assert.equal(resDefault.error, undefined, 'dotenv.config() must not report an error');
assert.ok(resDefault.parsed, 'dotenv.config() returns parsed key-value map');
assert.equal(
resDefault.parsed.API_ENDPOINT,
'https://api.example.org/v2',
'result.parsed reflects file value even when process.env is not modified',
);
assert.equal(resDefault.parsed.EMPTY_TOKEN, 'super-secret-token');
assert.equal(resDefault.parsed.NEW_KEY, 'fresh-value');
// But process.env silently preserves preexisting values (including empty strings)
assert.equal(
process.env.API_ENDPOINT,
'https://api.example.com/v1',
'default override: false must NOT overwrite existing process.env variables',
);
assert.equal(
process.env.EMPTY_TOKEN,
'',
'empty string in process.env counts as defined and is not overwritten',
);
assert.equal(
process.env.NEW_KEY,
'fresh-value',
'previously unset keys are populated normally',
);
// -------------------------------------------------------------------------
// Test 2: Passing override: true explicitly allows overwriting process.env
// -------------------------------------------------------------------------
const resOverride = loadEnv({ path: envMainPath, override: true });
assert.equal(resOverride.error, undefined);
assert.equal(
process.env.API_ENDPOINT,
'https://api.example.org/v2',
'override: true must update process.env with the file value',
);
assert.equal(
process.env.EMPTY_TOKEN,
'super-secret-token',
'override: true must overwrite empty string presets in process.env',
);
// -------------------------------------------------------------------------
// Test 3: Layered configuration files (.env.base + .env.local)
// -------------------------------------------------------------------------
// 3a. Without override: true, the second file (.env.local) silently fails to override keys from .env.base
delete process.env.DB_HOST;
delete process.env.DB_PORT;
delete process.env.BASE_ONLY;
delete process.env.LOCAL_ONLY;
loadLayeredEnv([envBasePath, envLocalPath], false);
assert.equal(
process.env.DB_HOST,
'db.example.test',
'without override: true, .env.local cannot overwrite DB_HOST set by .env.base',
);
assert.equal(process.env.DB_PORT, '5432');
assert.equal(process.env.BASE_ONLY, 'base-config');
assert.equal(
process.env.LOCAL_ONLY,
'local-config',
'new keys from .env.local are still added',
);
// 3b. With override: true, .env.local successfully overrides overlapping keys from .env.base
delete process.env.DB_HOST;
delete process.env.DB_PORT;
delete process.env.BASE_ONLY;
delete process.env.LOCAL_ONLY;
loadLayeredEnv([envBasePath, envLocalPath], true);
assert.equal(
process.env.DB_HOST,
'localhost',
'with override: true, .env.local overrides DB_HOST',
);
assert.equal(process.env.DB_PORT, '5432', 'unaltered keys from .env.base remain intact');
assert.equal(process.env.BASE_ONLY, 'base-config');
assert.equal(process.env.LOCAL_ONLY, 'local-config');
// -------------------------------------------------------------------------
// Test 4: Preloading with node -r dotenv/config and DOTENV_CONFIG_OVERRIDE
// -------------------------------------------------------------------------
// 4a. Without DOTENV_CONFIG_OVERRIDE, shell environment variables win over preloaded file
const childEnvWithoutOverride = {
...process.env,
DB_HOST: 'shell.example.test',
DOTENV_CONFIG_PATH: envBasePath,
DOTENV_CONFIG_QUIET: 'true',
};
delete childEnvWithoutOverride.DOTENV_CONFIG_OVERRIDE;
const r1 = spawnSync(
process.execPath,
['-r', 'dotenv/config', '-e', 'process.stdout.write(process.env.DB_HOST)'],
{ cwd: root, env: childEnvWithoutOverride, encoding: 'utf8' },
);
assert.equal(r1.status, 0, `preload failed: ${r1.stderr}`);
assert.equal(
r1.stdout.trim(),
'shell.example.test',
'preloading without DOTENV_CONFIG_OVERRIDE leaves shell variable intact',
);
// 4b. With DOTENV_CONFIG_OVERRIDE=true, file values override shell environment variables
const childEnvWithOverride = {
...process.env,
DB_HOST: 'shell.example.test',
DOTENV_CONFIG_PATH: envBasePath,
DOTENV_CONFIG_OVERRIDE: 'true',
DOTENV_CONFIG_QUIET: 'true',
};
const r2 = spawnSync(
process.execPath,
['-r', 'dotenv/config', '-e', 'process.stdout.write(process.env.DB_HOST)'],
{ cwd: root, env: childEnvWithOverride, encoding: 'utf8' },
);
assert.equal(r2.status, 0, `preload with override failed: ${r2.stderr}`);
assert.equal(
r2.stdout.trim(),
'db.example.test',
'preloading with DOTENV_CONFIG_OVERRIDE=true updates process.env to file value',
);
console.log(
'CONTRACT PASS: dotenv preserves existing process.env variables unless override: true or DOTENV_CONFIG_OVERRIDE is enabled',
);
} finally {
fs.rmSync(envMainPath, { force: true });
fs.rmSync(envBasePath, { force: true });
fs.rmSync(envLocalPath, { force: true });
}