vlucas/phpdotenv · error · InvalidPathException

Unable to read any of the environment file(s) at [%s].

Error message

Unable to read any of the environment file(s) at [%s].

What it means

FileStore::read() (src/Store/FileStore.php:68) throws Dotenv\Exception\InvalidPathException after Reader::read() returned zero readable files: every registered path (directory + name combination) failed silently via @file_get_contents and was omitted, so imploding nothing is impossible. With shortCircuit=true (the default) reading stops at the first success; the exception means not even one candidate existed or was readable. safeLoad() exists specifically to absorb this exception.

Source

Thrown at src/Store/FileStore.php:68

     * Read the content of the environment file(s).
     *
     * @throws \Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidPathException
     *
     * @return string
     */
    public function read()
    {
        if ($this->filePaths === []) {
            throw new InvalidPathException('At least one environment file path must be provided.');
        }

        $contents = Reader::read($this->filePaths, $this->shortCircuit, $this->fileEncoding);

        if (\count($contents) > 0) {
            return \implode("\n", $contents);
        }

        throw new InvalidPathException(
            \sprintf('Unable to read any of the environment file(s) at [%s].', \implode(', ', $this->filePaths))
        );
    }
}

View on GitHub (pinned to 416df70283)

Solutions

  1. Verify the exact candidate path: file_exists($dir.'/.env') (and is_readable()) before load().
  2. Use absolute paths anchored in the code: Dotenv::createImmutable(dirname(__DIR__)) or dirname(__DIR__, 2) for bin/ subdirs.
  3. If the file is optional, switch load() to safeLoad(), which suppresses InvalidPathException and returns [].
  4. Check getcwd() in the failing context (cron, container, test runner) and chdir or pass absolute dirs.

Example fix

// before
$dotenv = Dotenv::createImmutable(__DIR__)->load(); // in bin/console: __DIR__ is bin/, .env is one level up

// after
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 2))->safeLoad(); // project root, optional file
Defensive patterns

Strategy: fallback

Validate before calling

// Check the exact candidates FileStore will try (path + name):
$candidates = [$dir . '/.env'];
foreach ($candidates as $file) {
    if (file_exists($file) && is_readable($file)) {
        Dotenv::createImmutable($dir)->load();
        return;
    }
}
// optional file: fall through to real-environment-only mode

Try / catch

use Dotenv\Exception\InvalidPathException;

try {
    Dotenv::createImmutable(dirname(__DIR__, 2))->load();
} catch (InvalidPathException $e) {
    // no env file found: either proceed with real env vars only, or print setup guidance and exit(1)
    fwrite(STDERR, 'No .env found. Copy .env.example to .env and configure it.' . \PHP_EOL);
    exit(1);
}
// Or simply: Dotenv::createImmutable($dir)->safeLoad(); // library-suppressed InvalidPathException

Prevention

When it happens

Trigger: Dotenv::createImmutable(__DIR__)->load() when __DIR__/.env does not exist; passing custom $names ('.env.local') that match nothing; paths resolved against a different getcwd() (CLI bin scripts, PHPUnit, cron, docker WORKDIR changes); file exists but is unreadable due to permissions; shortCircuit=false with multiple names and none present.

Common situations: Fresh clone without copying .env.example to .env; deploys where cwd is not the project root; running tests from a subdirectory; .env excluded by .gitignore/.dockerignore; permission changes after hardening.

Related errors


AI-assisted analysis of vlucas/phpdotenv@416df70283 (2026-08-21). Data as JSON: /api/errors/be83fef358bb89d9. Report an issue: GitHub.