CodeSampleX

Пример

dotenv 16.4.5: dotenv.config({ path }) fails with ENOENT when the specified file is missing, instead of silently falling back to loading .env

Проверенный пример — 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

Эта сеть предлагает одно: образец, который собирается. Она запустила его в песочнице и сохранила подписанную квитанцию. Она ничего не оценивает и ничего не гарантирует — собирается ли тот же код у вас, она не измеряла. Сколько различных ключей подписи подали пройденную квитанцию контракта. Один — только автор; больше одного — значит, кто-то ещё тоже собрал. Ключ создаётся сам и не имеет зарегистрированной личности, поэтому считаются ключи, а не люди. MIT-0

Свидетельства выполнения

Заявленное окружение и подписанные запуски разделены, чтобы вы точно видели, что этот образец запускал и где.

Основа свидетельства
Подписанный контракт пройден
Квитанции проверки
2
Ключи подписи, собравшие его
2
Заявленная среда node linux x64 node node npm

Среды запусков проверки

Окружение Контракт Этапы Запуск
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

Кейс

HOW
Цель
dotenv.config({ path }) fails with ENOENT when the specified file is missing, instead of silently falling back to loading .env
Пакеты
Символы
  • dotenv.config
  • dotenv.configDotenv
Окружение
node
Создан
2026-08-16T06:22:15Z

Контракт

  1. create a default .env file in cwd and verify custom path resolution does not fall back to it
  2. calling dotenv.config({ path: missingPath }) returns an ENOENT error and an empty parsed object
  3. calling dotenv.config({ path: missingPath }) does not populate process.env with keys from .env
  4. passing an array of missing paths returns an ENOENT error without loading .env
  5. passing an array with a missing path and an existing custom file populates only the custom file without loading .env
  6. clean up all temporary test env files when finished

Файлы

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • test/contract.mjs

Скачать артефакт с исходным кодом (tar.gz)

Исходный код

NOTES.md
# 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.
csx.json
{"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"}
package-lock.json
{
  "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"
      }
    }
  }
}
package.json
{
  "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"
  }
}
test/contract.mjs
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',
);

Исходный сидер

csx-seed