vlucas/phpdotenv · error · InvalidEncodingException

Conversion from encoding [%s] failed.

Error message

Conversion from encoding [%s] failed.

What it means

In Str::utf8() (src/Util/Str.php:49) mbstring's mb_convert_encoding() is run with error suppression; if it returns a non-string (false on failure), the Result becomes an error and Reader::readFromFile() throws Dotenv\Exception\InvalidEncodingException (src/Store/File/Reader.php:77). Unlike error [7] the encoding name was accepted — the conversion itself failed, meaning the file bytes could not be converted to UTF-8 under the declared (or auto-detected) encoding. The message prints 'NULL' when no encoding was given.

Source

Thrown at src/Store/File/Reader.php:77

    /**
     * Read the given file.
     *
     * @param string      $path
     * @param string|null $encoding
     *
     * @throws \Dotenv\Exception\InvalidEncodingException
     *
     * @return \PhpOption\Option<string>
     */
    private static function readFromFile(string $path, ?string $encoding = null)
    {
        /** @var Option<string> */
        $content = Option::fromValue(@\file_get_contents($path), false);

        return $content->flatMap(static function (string $content) use ($encoding) {
            return Str::utf8($content, $encoding)->mapError(static function (string $error) {
                throw new InvalidEncodingException($error);
            })->success();
        });
    }
}

View on GitHub (pinned to 416df70283)

Solutions

  1. Open the file in an editor, re-save it as plain UTF-8, and drop the fileEncoding argument (null auto-detect then works).
  2. If you keep a non-UTF-8 file, verify the declared encoding matches reality: mb_check_encoding(file_get_contents($path), $encoding).
  3. Inspect the first bytes (BOM) and file size — truncated multibyte files convert fine only after repair.
  4. Check for stray binary characters: bin2hex the region around any pasted secret.

Example fix

// before
$dotenv = Dotenv::createImmutable(__DIR__, null, true, 'UTF-16')->load();
// file is actually UTF-8 -> InvalidEncodingException: Conversion from encoding [UTF-16] failed.

// after (re-save .env as UTF-8, then)
$dotenv = Dotenv::createImmutable(__DIR__)->load();
Defensive patterns

Strategy: validation

Validate before calling

// Prove the file converts cleanly before bootstrapping Dotenv:
$path = $dir . '/.env';
$content = file_get_contents($path);
if ($content !== false && $encoding !== null && !mb_check_encoding($content, $encoding)) {
    throw new RuntimeException(".env is not valid {$encoding}; re-save it as UTF-8");
}
// For auto-detect (encoding === null), a round-trip sanity check:
if ($content !== false && @mb_convert_encoding($content, 'UTF-8') === false) {
    throw new RuntimeException('.env content could not be converted to UTF-8');
}

Try / catch

use Dotenv\Exception\InvalidEncodingException;

try {
    Dotenv::createImmutable($dir, null, true, $encoding)->load();
} catch (InvalidEncodingException $e) {
    // 'Conversion from encoding [X] failed.' -> the bytes do not match the declared encoding
    // re-save the file as UTF-8 and retry once with encoding omitted; then fail hard
}

Prevention

When it happens

Trigger: Declaring fileEncoding='UTF-16' while the file is actually UTF-8/corrupt/truncated (bad BOM, odd byte length); leaving encoding null so mb_convert_encoding auto-detection guesses wrong on mixed or binary content; a .env containing binary garbage or a truncated multibyte sequence; file mutated by a tool that mangled bytes.

Common situations: Files written by Windows editors claiming UTF-16 but saved differently; CI checkouts applying encoding-filters or CRLF manglers; secrets pasted from a password manager inserting odd bytes; partial uploads of the env file.

Related errors


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