octobercms/october · error · ApplicationException

system::lang.media.invalid_path_encoding

Error message

system::lang.media.invalid_path_encoding

What it means

MediaLibrary::validatePath() normalizes a path and then requires it to be valid UTF-8: mb_check_encoding($path, 'UTF-8') must pass. A byte string in another encoding (Latin-1/Windows-1252 0xE9 for 'é', GBK multibyte, raw binary) triggers invalid_path_encoding, with the offending path scrubbed via mb_scrub for safe display. This guard exists because every later regex uses the /u modifier, which fails on invalid UTF-8, and storage backends assume UTF-8 keys.

Source

Thrown at modules/media/classes/MediaLibrary.php:495

     * validatePath checks if file path doesn't contain any substrings that would pose a security
     * threat. Returns a normalized path. Throws an exception if the path is not valid. An option
     * is provided, if only normalization is needed without validation.
     * @param string $path
     * @param bool $normalizeOnly
     * @return string
     */
    public static function validatePath($path, $normalizeOnly = false): string
    {
        $path = str_replace('\\', '/', $path);
        $path = '/'.trim($path, '/');

        if ($normalizeOnly) {
            return $path;
        }

        // Reject paths that are not valid UTF-8
        if (!mb_check_encoding($path, 'UTF-8')) {
            throw new ApplicationException(Lang::get('system::lang.media.invalid_path_encoding', ['path' => mb_scrub($path)]));
        }

        // Reject control, format and other invisible characters
        if (preg_match('/[\p{C}]/u', $path)) {
            throw new ApplicationException(Lang::get('system::lang.media.invalid_path', compact('path')));
        }

        // Reject characters reserved by file systems and URLs
        if (preg_match('/[<>:"|?*]/', $path)) {
            throw new ApplicationException(Lang::get('system::lang.media.invalid_path', compact('path')));
        }

        $regexDirectorySeparator = preg_quote('/', '#');
        $regexDot = preg_quote('.', '#');
        $regex = [
            // Beginning of path
            '(^'.$regexDot.'+?'.$regexDirectorySeparator.')',

View on GitHub (pinned to b608633a7e)

Solutions

  1. Convert the encoding before calling the API: $path = mb_convert_encoding($path, 'UTF-8', 'Windows-1252'); (or iconv with //TRANSLIT)
  2. Fix the source of the path: make DB columns utf8mb4 and store/emit UTF-8 everywhere
  3. For display of already-corrupt data, pass mb_scrub($path) so invalid sequences are substituted instead of crashing

Example fix

// before — $path came from a latin1 source
$clean = MediaLibrary::validatePath($path); // throws invalid_path_encoding

// after — convert first, then validate
$clean = MediaLibrary::validatePath(mb_convert_encoding($path, 'UTF-8', 'Windows-1252'));
Defensive patterns

Strategy: type-guard

Type guard

function isUtf8Path(mixed $path): bool {
    return is_string($path) && mb_check_encoding($path, 'UTF-8');
}

// usage
if (!isUtf8Path($path)) {
    $path = mb_convert_encoding($path, 'UTF-8', 'Windows-1252');
}

Try / catch

try {
    $clean = MediaLibrary::validatePath($path);
} catch (ApplicationException $e) {
    // invalid_path_encoding vs invalid_path can be distinguished by message
    $path = mb_scrub($path);
    $clean = MediaLibrary::validatePath($path);
}

Prevention

When it happens

Trigger: Calling a media API with a Windows-1252-encoded filename from a legacy integration; paths read from an old latin1 database column; a client percent-decoding URL-encoded bytes into non-UTF-8 sequences; binary garbage in a crafted request.

Common situations: Migrating media indexes from old systems; scripts that read filenames from a legacy filesystem without iconv; API consumers defaulting to a single-byte charset.

Related errors


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/3b7e23c2c1f08d21. Report an issue: GitHub.