{"record":{"id":"36c5e7bd8995d9e0","repo":"vlucas/phpdotenv","slug":"lexer-encountered-unexpected-character-s","errorCode":null,"errorMessage":"Lexer encountered unexpected character [%s].","messagePattern":"Lexer encountered unexpected character \\[(.+?)\\]\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/Parser/Lexer.php","lineNumber":50,"sourceCode":"     * handling, for performance reasons.\n     *\n     * @param string $content\n     *\n     * @return \\Generator<string>\n     */\n    public static function lex(string $content)\n    {\n        static $regex;\n\n        if ($regex === null) {\n            $regex = '(('.\\implode(')|(', self::PATTERNS).'))A';\n        }\n\n        $offset = 0;\n\n        while (isset($content[$offset])) {\n            if (!\\preg_match($regex, $content, $matches, 0, $offset)) {\n                throw new \\Error(\\sprintf('Lexer encountered unexpected character [%s].', $content[$offset]));\n            }\n\n            $offset += \\strlen($matches[0]);\n\n            yield $matches[0];\n        }\n    }\n}\n","sourceCodeStart":32,"sourceCodeEnd":59,"githubUrl":"https://github.com/vlucas/phpdotenv/blob/416df702837983f8d5ff48c9c3fee4f5f57b980b/src/Parser/Lexer.php#L32-L59","documentation":"Lexer::lex() (src/Parser/Lexer.php:50) tokenizes entry VALUES (EntryParser::parseValue feeds it each value) by matching one of eight anchored patterns covering newlines, whitespace, backslash, quotes, '#', '$', parens, and runs of any other byte; when preg_match fails at an offset it throws PHP \\Error — not an Exception — naming the offending character. Two consequences: it is not caught by 'catch (Exception $e)' and it bypasses the Result pipeline that normally produces InvalidFileException [0]. In this exact pattern set every byte is covered, so in practice the throw fires only when the PCRE engine itself fails (preg_match returning false, e.g. limit exhaustion on pathological input); in earlier/modified 5.x pattern sets it surfaced for characters outside the token set.","triggerScenarios":"Any ->load()/safeLoad()/Dotenv::parse() call whose VALUE region makes the anchored preg_match fail: PCRE errors (backtrack/recursion limits hit on adversarial or degenerate content); content that breaks a customized Lexer::PATTERNS in a forked/patched vendor copy; older 5.x releases where stray characters (control bytes, unusual punctuation) matched no token. Note plain syntax mistakes (spaces, bad escapes, unclosed quotes) normally surface as InvalidFileException [0] instead — the lexer tokenizes them fine and EntryParser rejects them.","commonSituations":"Fuzzed or machine-generated .env content; vendor tree patched by a Composer patch changing the lexer; extremely long single-line values in older versions; upgrading across 5.x point releases where PATTERNS changed.","solutions":["Catch \\Error (or \\Throwable) around load() — 'catch (Exception)' will miss it.","Search the .env for the character named between [ and ] and remove/quote it.","Re-save the .env from a plain-text editor as UTF-8 to strip control/binary bytes.","composer update vlucas/phpdotenv within your major version — the current PATTERNS set covers all bytes.","If you patch Lexer::PATTERNS, verify every byte 0x00-0xFF still matches at least one alternative."],"exampleFix":"// before\ntry {\n    $dotenv->load();\n} catch (\\Exception $e) {       \\Error escapes this\n    log($e->getMessage());\n}\n\n// after\ntry {\n    $dotenv->load();\n} catch (\\Throwable $e) {       \\ catches both \\Error and the Dotenv exceptions\n    log($e->getMessage());\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// The lexer throws PHP \\Error, not an Exception — catch \\Throwable or \\Error:\ntry {\n    Dotenv::createImmutable($dir)->load();\n} catch (\\Dotenv\\Exception\\InvalidFileException $e) {\n    // normal syntax problems, see error 0\n} catch (\\Error $e) {\n    if (str_contains($e->getMessage(), 'Lexer encountered unexpected character')) {\n        // isolate the named character, normalize/re-save the .env as UTF-8, then retry once\n    }\n    throw $e;\n}","preventionTips":["Bootstrap wrappers should catch \\Throwable, not \\Exception — \\Error escapes Exception-only catches.","Keep vendor/ unpatched, or byte-test any modified Lexer::PATTERNS for full 0x00-0xFF coverage.","Sanitize machine-generated .env content (strip control bytes) before it reaches the parser.","Stay current within your phpdotenv 5.x line — the shipped pattern set covers every byte."],"tags":["dotenv","lexer","php-error","parsing","php"],"backgroundTag":"dotenv-syntax-error","analyzedSha":"416df702837983f8d5ff48c9c3fee4f5f57b980b","analyzedAt":"2026-08-21T01:19:52.946Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}