twigphp/Twig · error · LoaderError

Looks like you try to load a template outside configured…

Error message

Looks like you try to load a template outside configured directories (%s).

What it means

FilesystemLoader resolves template names relative to configured root directories while forbidding traversal above them (via "../" segments tracked as a level counter in validateName). If the normalized path walks out of every root, Twig throws this LoaderError to prevent reading arbitrary files outside the template directories.

Solutions

  1. Reject or sanitize template names containing ".." segments before passing them to the loader.
  2. Ensure the loader root(s) are set correctly (new FilesystemLoader($rootDir), addPath()) so relative names resolve inside the project.
  3. Whitelist template names (regex or fixed set) instead of accepting raw input.
  4. If a legitimate template lives outside the root, register its directory with addPath() instead of using "../" in names.
  5. As defense in depth, use the sandbox extension when templates are user-influenced.

Example fix

// before
$page = $_GET['page']; // "../../etc/passwd"
echo $twig->render($page . '.twig');
// after
$page = basename((string) $_GET['page']); // strip traversal
if (!preg_match('/^[\w\-]+$/', $page)) {
    throw new InvalidArgumentException('Invalid page.');
}
echo $twig->render($page . '.twig');
Defensive patterns

Strategy: validation

Validate before calling

function isTemplateInsideRoots(string $name): bool {
    $name = ltrim(str_replace('\\', '/', $name), '/');
    $level = 0;
    foreach (explode('/', $name) as $part) {
        if ($part === '..') { $level--; }
        elseif ($part !== '.') { $level++; }
        if ($level < 0) { return false; }
    }
    return true;
}

Try / catch

try {
    $html = $twig->render($name);
} catch (\Twig\Error\LoaderError $e) {
    if (str_contains($e->getMessage(), 'outside configured directories')) {
        // treat as forbidden path; log and return 404
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling findTemplate (via render/load/getSourceContext) with a name whose normalized path escapes the loader roots, e.g. "../../etc/passwd" or "../secrets.html.twig" — typically when the name comes from user input.

Common situations: Path traversal attacks on template-name parameters; dynamic template paths miscomputed with a leading ".."; misconfigured loader roots so legitimate relative paths appear to escape.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13). Data as JSON: /api/errors/dcd18f2f3db04db3. Report an issue: GitHub.

Appendix: source

Thrown at src/Loader/FilesystemLoader.php:275

    private function validateName(string $name): void
    {
        if (str_contains($name, "\0")) {
            throw new LoaderError('A template name cannot contain NUL bytes.');
        }

        $name = ltrim($name, '/');
        $parts = explode('/', $name);
        $level = 0;
        foreach ($parts as $part) {
            if ('..' === $part) {
                --$level;
            } elseif ('.' !== $part) {
                ++$level;
            }

            if ($level < 0) {
                throw new LoaderError(\sprintf('Looks like you try to load a template outside configured directories (%s).', $name));
            }
        }
    }

    private function isAbsolutePath(string $file): bool
    {
        return strspn($file, '/\\', 0, 1)
            || (\strlen($file) > 3 && ctype_alpha($file[0])
                && ':' === $file[1]
                && strspn($file, '/\\', 2, 1)
            )
            || null !== parse_url($file, \PHP_URL_SCHEME)
        ;
    }
}

View on GitHub (pinned to a414c3a491)