twigphp/Twig · error · LoaderError

Unable to find template

Error message

Unable to find template "%s" (looked into: %s).

What it means

findTemplate() searched every registered path for the namespace and found no file matching the template name. The thrown message lists all directories that were looked into, and the failure is memoized in errorCache for subsequent lookups of the same name.

Solutions

  1. Create the template file in one of the directories listed in 'looked into:'.
  2. Fix the template name (spelling, extension, case) to match the actual file.
  3. Add the directory containing the file via addPath().
  4. Check filename case matches exactly — production filesystems are case-sensitive.

Example fix

// before
$twig->render('user/Profile.html.twig'); // actual file is profile.html.twig
// after
$twig->render('user/profile.html.twig');
Defensive patterns

Strategy: try-catch

Validate before calling

if (!$loader->exists($name)) { // surface a friendly 404 or fallback instead of a raw LoaderError }

Try / catch

try { return $twig->render($name, $ctx); } catch (Twig\Error\LoaderError $e) { if (str_contains($e->getMessage(), 'Unable to find template')) { return $twig->render('errors/404.html.twig'); } throw $e; }

Prevention

When it happens

Trigger: Calling render()/getSourceContext()/getCacheKey()/exists() for a template whose file does not exist in any registered path — wrong filename, wrong extension, or file simply missing.

Common situations: Missing file after deploy; case-sensitivity differences between local (case-insensitive FS) and production (Linux); wrong template extension; templates stored outside registered paths.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at src/Loader/FilesystemLoader.php:234

                $path = $this->rootPath.$path;
            }

            if (is_file($path.'/'.$shortname)) {
                if (false !== $realpath = realpath($path.'/'.$shortname)) {
                    return $this->cache[$name] = $realpath;
                }

                return $this->cache[$name] = $path.'/'.$shortname;
            }
        }

        $this->errorCache[$name] = \sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]));

        if (!$throw) {
            return null;
        }

        throw new LoaderError($this->errorCache[$name]);
    }

    private function normalizeName(string $name): string
    {
        return preg_replace('#/{2,}#', '/', str_replace('\\', '/', $name));
    }

    private function parseName(string $name, string $default = self::MAIN_NAMESPACE): array
    {
        if (isset($name[0]) && '@' == $name[0]) {
            if (false === $pos = strpos($name, '/')) {
                throw new LoaderError(\sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
            }

            $namespace = substr($name, 1, $pos - 1);
            $shortname = substr($name, $pos + 1);

            return [$namespace, $shortname];

View on GitHub (pinned to a414c3a491)