vlucas/phpdotenv · critical · ValidationException

One or more environment variables failed assertions: %s.

Error message

One or more environment variables failed assertions: %s.

What it means

Validator::assert() (src/Validator.php:173) is the funnel behind required(), notEmpty(), isInteger(), isBoolean(), allowedValues(), allowedRegexValues() and custom assertions: it evaluates a callback per variable against the repository and, if any returned false, aggregates 'VAR <reason>' fragments (e.g. 'DB_HOST is missing', 'PORT is not an integer') and throws Dotenv\Exception\ValidationException. One exception therefore reports every failing variable at once. required() itself is just assert(fn ($v) => $v !== null, 'is missing'), and assertNullable-based checks skip variables whose value is null.

Source

Thrown at src/Validator.php:173

     * @param callable(?string):bool $callback
     * @param string                 $message
     *
     * @throws \Dotenv\Exception\ValidationException
     *
     * @return \Dotenv\Validator
     */
    public function assert(callable $callback, string $message)
    {
        $failing = [];

        foreach ($this->variables as $variable) {
            if ($callback($this->repository->get($variable)) === false) {
                $failing[] = \sprintf('%s %s', $variable, $message);
            }
        }

        if (\count($failing) > 0) {
            throw new ValidationException(\sprintf(
                'One or more environment variables failed assertions: %s.',
                \implode(', ', $failing)
            ));
        }

        return $this;
    }

    /**
     * Assert that the callback returns true for each variable.
     *
     * Skip checking null variable values.
     *
     * @param callable(string):bool $callback
     * @param string                $message
     *
     * @throws \Dotenv\Exception\ValidationException
     *

View on GitHub (pinned to 416df70283)

Solutions

  1. Read the message — it lists each failing variable with its reason; fix those entries in .env or the real environment first.
  2. Check spelling of the variable names in required([...]) against the .env keys.
  3. Make optional variables non-fatal with ifPresent([...])->notEmpty() instead of required(...).
  4. Allow empty values where legitimate: stop at ->required() and drop ->notEmpty().
  5. For guided setup, catch the exception and print 'copy .env.example to .env and fill it in', exiting non-zero.

Example fix

// before
$dotenv->required(['APP_KEY', 'DB_HOST', 'MAIL_FROM'])->notEmpty();

// after
$dotenv->required(['APP_KEY', 'DB_HOST'])->notEmpty(); // hard dependencies
$dotenv->ifPresent(['MAIL_FROM'])->notEmpty();          // optional but must not be blank when set
Defensive patterns

Strategy: try-catch

Validate before calling

// Quick pre-boot presence check for hard dependencies (mirrors required()):
$required = ['APP_KEY', 'DB_HOST'];
$missing = array_filter(
    $required,
    static fn (string $name): bool => $_ENV[$name] ?? getenv($name) === false
);
if ($missing !== []) {
    fwrite(STDERR, 'Missing env vars: ' . implode(', ', $missing) . \PHP_EOL);
    exit(1);
}

Try / catch

use Dotenv\Exception\ValidationException;

try {
    $dotenv->required(['APP_KEY', 'DB_HOST'])->notEmpty();
} catch (ValidationException $e) {
    // message already lists every failing var and reason, e.g.
    // 'One or more environment variables failed assertions: APP_KEY is missing, DB_HOST is empty.'
    fwrite(STDERR, $e->getMessage() . \PHP_EOL . 'Hint: copy .env.example to .env and fill it in.' . \PHP_EOL);
    exit(1); // fail the boot/deploy instead of running half-configured
}

Prevention

When it happens

Trigger: Dotenv::createImmutable($dir)->load()->required('DB_HOST') when DB_HOST is absent (-> 'DB_HOST is missing'); ->required('APP_KEY')->notEmpty() when APP_KEY is unset, '' or whitespace-only (notEmpty trims before checking); ->required('PORT')->isInteger() with PORT=8080.5; allowedValues(['staging','prod']) with APP_ENV=dev; calling ->required() after safeLoad() when no file was read at all.

Common situations: New deploy/container missing secrets; fresh clone without .env; typo in the required() list ('DB_H0ST'); vars defined in .env but a different file was loaded (wrong path/name); whitespace-only values from a template; CI runners lacking environment configuration.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


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