Ejemplo
dotenv 16.4.5: dotenv.config({ path }) fails with ENOENT when the specified file is missing, instead of silently falling back to loading .env
Muestra verificada para npm dotenv 16.4.5: dotenv.config({ path }) fails with ENOENT when the specified file is missing, instead of silently falling back to…
sha256:81c1f10d06436657fe935460d23e4a48f80c8b300a1fa96baa4686ef600468d4
Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido.
Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas.
MIT-0
Evidencia de ejecución
El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.
- Base de evidencia
- Contrato firmado aprobado
- Recibos de verificación
- 2
- Claves de firma que lo compilaron
- 2
Entorno declarado
node linux x64 node node npm
Entornos de las ejecuciones de verificación
| Entorno | Contrato | Etapas | Ejecución |
|---|---|---|---|
| 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
- dotenv.config({ path }) fails with ENOENT when the specified file is missing, instead of silently falling back to loading .env
- Paquetes
- Símbolos
-
- dotenv.config
- dotenv.configDotenv
- Entorno
- node
- Creado
- 2026-08-16T06:22:15Z
Contrato
- create a default .env file in cwd and verify custom path resolution does not fall back to it
- calling dotenv.config({ path: missingPath }) returns an ENOENT error and an empty parsed object
- calling dotenv.config({ path: missingPath }) does not populate process.env with keys from .env
- passing an array of missing paths returns an ENOENT error without loading .env
- passing an array with a missing path and an existing custom file populates only the custom file without loading .env
- clean up all temporary test env files when finished
Archivos
- NOTES.md
- csx.json
- package-lock.json
- package.json
- test/contract.mjs
Código fuente
# Notes
## search_known_solution result
`search_known_solution` matched sample `sha256:51dd5f35fb9c7be3bc11ebc83f2c01779e197c88f316ecd1ff3173bacbe66884` ("process.env is undefined in an ESM module even though dotenv.config() ran and .env exists"), which focuses on ESM static import hoisting rather than custom path resolution regressions.
## What a model would have written instead
A model writing multi-environment or isolated loader code writes `dotenv.config({ path: customPath })` assuming `dotenv` strictly reads only `customPath` and returns an error without touching `process.env` if the file does not exist.
## How the wrong version fails
Silently with a green build — in `dotenv@16.4.3` and `dotenv@16.4.4`, `config({ path: missingPath })` silently falls back to auto-discovering `.env` in the current working directory, returning `{ parsed: { ... }, error: undefined }` and leaking default `.env` variables into `process.env` without any error.
{"case":{"caseId":"case:sha256:b4a5ca7cb81c99348a693f28e8f948f383a3da62a80f9761a3a6cc435eba1956","contract":["create a default .env file in cwd and verify custom path resolution does not fall back to it","calling dotenv.config({ path: missingPath }) returns an ENOENT error and an empty parsed object","calling dotenv.config({ path: missingPath }) does not populate process.env with keys from .env","passing an array of missing paths returns an ENOENT error without loading .env","passing an array with a missing path and an existing custom file populates only the custom file without loading .env","clean up all temporary test env files when finished"],"goal":"dotenv.config({ path }) fails with ENOENT when the specified file is missing, instead of silently falling back to loading .env","kind":"HOW","packages":["pkg:npm/dotenv@16.4.5"],"schemaVersion":1,"symbols":["dotenv.config","dotenv.configDotenv"]},"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@16.4.5"],"schemaVersion":1,"symbols":["dotenv.config","dotenv.configDotenv"],"verifierAdapter":"node-typescript@1"}
{
"name": "csx-sample-dotenv-custom-path-fallback-fix",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "csx-sample-dotenv-custom-path-fallback-fix",
"version": "1.0.0",
"license": "MIT-0",
"dependencies": {
"dotenv": "16.4.5"
}
},
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
}
}
}
{
"name": "csx-sample-dotenv-custom-path-fallback-fix",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT-0",
"scripts": {
"test": "node test/contract.mjs"
},
"dependencies": {
"dotenv": "16.4.5"
}
}
import { strict as assert } from 'node:assert';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import dotenv from 'dotenv';
const root = fileURLToPath(new URL('..', import.meta.url));
const defaultEnvPath = path.join(root, '.env');
const customEnvPath = path.join(root, '.env.custom');
const missingPath = path.join(root, '.env.nonexistent');
const missingArrayPathA = path.join(root, '.env.missing_a');
const missingArrayPathB = path.join(root, '.env.missing_b');
const DEFAULT_KEY = 'CSX_SAMPLE_DEFAULT_KEY';
const DEFAULT_VAL = 'default_from_dot_env';
const CUSTOM_KEY = 'CSX_SAMPLE_CUSTOM_KEY';
const CUSTOM_VAL = 'custom_from_file';
function cleanEnv() {
delete process.env[DEFAULT_KEY];
delete process.env[CUSTOM_KEY];
}
try {
// Setup: create a default .env file in the current working directory.
// In buggy versions (16.4.3-16.4.4), any missing custom path would silently
// fall back to reading this default .env file and polluting process.env.
fs.writeFileSync(defaultEnvPath, `${DEFAULT_KEY}=${DEFAULT_VAL}\n`);
fs.writeFileSync(customEnvPath, `${CUSTOM_KEY}=${CUSTOM_VAL}\n`);
// 1. Single missing path: dotenv.config({ path: missingPath })
cleanEnv();
const res1 = dotenv.config({ path: missingPath });
// Fixed behavior (16.4.5+):
// - Returns an error with code ENOENT
// - Does NOT parse default .env (parsed is empty)
// - Does NOT populate process.env from default .env
assert.ok(res1.error, 'missing custom path must return an error');
assert.equal(res1.error.code, 'ENOENT', 'error code must be ENOENT');
assert.deepEqual(res1.parsed, {}, 'parsed result must be empty when custom file is missing');
assert.equal(
process.env[DEFAULT_KEY],
undefined,
'default .env must NOT be loaded when a missing custom path is specified',
);
// 2. Array of missing paths: dotenv.config({ path: [missingA, missingB] })
cleanEnv();
const res2 = dotenv.config({ path: [missingArrayPathA, missingArrayPathB] });
// Fixed behavior:
// - Returns an error with code ENOENT
// - Does NOT fall back to reading default .env
// - Does NOT populate process.env
assert.ok(res2.error, 'array of missing paths must return an error');
assert.equal(res2.error.code, 'ENOENT', 'error code must be ENOENT');
assert.deepEqual(res2.parsed, {}, 'parsed result must be empty when array paths are missing');
assert.equal(
process.env[DEFAULT_KEY],
undefined,
'default .env must NOT be loaded when all array paths are missing',
);
// 3. Array with one missing path and one existing custom path:
// dotenv.config({ path: [missingA, customEnvPath] })
cleanEnv();
const res3 = dotenv.config({ path: [missingArrayPathA, customEnvPath] });
// Fixed behavior:
// - Loads customEnvPath successfully and populates CUSTOM_KEY
// - Records ENOENT for missingArrayPathA in error property
// - Does NOT touch or load default .env (DEFAULT_KEY remains undefined)
assert.equal(res3.parsed[CUSTOM_KEY], CUSTOM_VAL, 'custom file values must be parsed');
assert.equal(process.env[CUSTOM_KEY], CUSTOM_VAL, 'custom file values must be loaded into process.env');
assert.equal(
process.env[DEFAULT_KEY],
undefined,
'default .env must NOT be loaded when array has an existing custom path',
);
assert.ok(res3.error, 'missing file in array must be recorded in error property');
assert.equal(res3.error.code, 'ENOENT', 'error code must be ENOENT');
// 4. Default behavior (no path specified):
cleanEnv();
const res4 = dotenv.config();
assert.equal(res4.error, undefined, 'default config() must load .env without error');
assert.equal(res4.parsed[DEFAULT_KEY], DEFAULT_VAL, 'default .env keys must be parsed');
assert.equal(process.env[DEFAULT_KEY], DEFAULT_VAL, 'default .env keys must be populated');
} finally {
// Cleanup test files
cleanEnv();
fs.rmSync(defaultEnvPath, { force: true });
fs.rmSync(customEnvPath, { force: true });
}
console.log(
'CONTRACT PASS: dotenv.config({ path }) strictly respects specified path and does not fall back to .env',
);