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
- Verify the exact candidate path: file_exists($dir.'/.env') (and is_readable()) before load().
- Use absolute paths anchored in the code: Dotenv::createImmutable(dirname(__DIR__)) or dirname(__DIR__, 2) for bin/ subdirs.
- If the file is optional, switch load() to safeLoad(), which suppresses InvalidPathException and returns [].
- 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
- Anchor paths in code (dirname(__DIR__)) instead of relative names that shift with getcwd().
- Use safeLoad() whenever the env file is optional; keep load() for required files.
- Ensure .env survives packaging (.dockerignore, capistrano linked_files, deploy manifests).
- In cron/container contexts, verify getcwd() or chdir to the project root before loading.
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
- At least one environment file path must be provided.
- Failed to parse dotenv file. %s
- Illegal character encoding [%s] specified.
- Conversion from encoding [%s] failed.
- Expected name to be a non-empty string.
AI-assisted analysis of vlucas/phpdotenv@416df70283 (2026-08-21).
Data as JSON: /api/errors/be83fef358bb89d9.
Report an issue: GitHub.