octobercms/october · error · ValidationException

cms::lang.cms_object.invalid_file

Error message

cms::lang.cms_object.invalid_file

What it means

ValidationException thrown while loading a theme language file: the requested file name resolved to a path that fails `FileHelper::validateInTheme()`, i.e. the path does not stay inside the theme directory. Theme lang files live in `themes/<theme>/lang/` and must be plain .json base names; any `../` traversal, absolute path, or otherwise escaping path is rejected before the file is read.

Source

Thrown at modules/cms/classes/Lang.php:209

        $foundTheme = $this->theme;

        if (!File::isFile($filePath)) {
            // Look at parent
            if ($parentTheme = $this->theme->getParentTheme()) {
                $foundTheme = $parentTheme;
                $filePath = $parentTheme->getPath().'/'.$this->dirName.'/'.$fileName;

                if (!File::isFile($filePath)) {
                    return null;
                }
            }
            else {
                return null;
            }
        }

        if (!FileHelper::validateInTheme($foundTheme, $filePath)) {
            throw new ValidationException(['fileName' =>
                LangHelper::get('cms::lang.cms_object.invalid_file', [
                    'name' => $fileName
                ])
            ]);
        }

        if (($content = @File::get($filePath)) === false) {
            return null;
        }

        $this->fileName = $fileName;
        $this->originalFileName = $fileName;
        $this->mtime = File::lastModified($filePath);
        $this->content = $content;
        $this->exists = true;

        return $this;
    }

View on GitHub (pinned to b608633a7e)

Solutions

  1. Pass only a plain base file name (e.g. 'en.json') — never directories, absolute paths, or traversal sequences.
  2. Sanitize incoming file names with `basename()` plus an allow-list regex before handing them to the Lang API.
  3. If you legitimately need nested lang files, use the theme's own directory structure support, not path strings in fileName.

Example fix

// before
$file = Lang::load($theme, '../config/secrets.json');

// after — base name only, must live inside themes/<theme>/lang/
$file = Lang::load($theme, 'en.json');
Defensive patterns

Strategy: validation

Validate before calling

$fileName = $request->input('fileName');
if (!is_string($fileName) || !preg_match('/^[\w\-\.]+\.json$/i', $fileName)) {
    throw new ValidationException(['fileName' => 'Invalid file name']);
}
// basename() as belt-and-braces, then load
$lang = Lang::load($theme, basename($fileName));

Try / catch

try {
    $lang = Lang::load($theme, $fileName);
} catch (Winter\Storm\Exception\ValidationException $e) {
    // invalid_file means the name escaped the theme: reject, never retry raw input
    return back()->withErrors($e->getErrors());
}

Prevention

When it happens

Trigger: Loading a lang object with a file name containing '../' (e.g. '../../config/app.json'), a leading slash / absolute path, or any segment that resolves outside the theme root; a crafted `fileName` parameter sent to a lang-file management endpoint.

Common situations: Path-traversal attempts against theme file APIs; an API client or import script passing OS-style paths instead of base file names; code reused from a different file API that assumed subdirectory support.

Related errors


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