twigphp/Twig · error · LoaderError

A template name cannot contain NUL bytes.

Error message

A template name cannot contain NUL bytes.

What it means

Twig's FilesystemLoader rejects template names containing NUL (\0) bytes before resolving them against configured directories. NUL bytes in paths can truncate the path in underlying filesystem calls and are a classic path-injection vector, so the loader throws a LoaderError immediately in validateName.

Solutions

  1. Trim or reject NUL bytes from the template name before passing it to the loader: $name = str_replace("\0", '', $name);
  2. Validate template names against an allowlist regex (e.g. /^[A-Za-z0-9_.\-]+$/) before loading.
  3. Never pass raw user input as a template name; map user choices to known template names.
  4. Log the offending name to find where the NUL byte enters (JSON decode, binary file read, etc.).

Example fix

// before
$template = $_GET['page'] . '.html.twig';
echo $twig->render($template);
// after
$name = $_GET['page'] . '.html.twig';
if (!preg_match('/^[A-Za-z0-9_.\-]+\.html\.twig$/', $name)) {
    throw new InvalidArgumentException('Invalid template name.');
}
echo $twig->render($name);
Defensive patterns

Strategy: validation

Validate before calling

if (str_contains($name, "\0")) {
    throw new InvalidArgumentException('Template name must not contain NUL bytes.');
}

Type guard

function isSafeTemplateName(string $name): bool {
    return $name !== '' && !str_contains($name, "\0")
        && preg_match('/^[A-Za-z0-9_.\-]+$/', $name) === 1;
}

Try / catch

try {
    $html = $twig->render($name);
} catch (\Twig\Error\LoaderError $e) {
    if (str_contains($e->getMessage(), 'NUL bytes')) {
        // sanitize or reject the template name
    }
    throw $e;
}

Prevention

When it happens

Trigger: Calling $loader->getSourceContext($name) / loadTemplate / findTemplate with a name containing a literal "\0", e.g. user-supplied template identifiers concatenated with binary data or unsanitized request input.

Common situations: Template names taken from HTTP query/body parameters or database values that contain binary junk; encoding bugs where "\u0000" from JSON or legacy PHP code (old null-byte trim behavior) leaks into the template name.

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/fe0becd5b521776c. Report an issue: GitHub.

Appendix: source

Thrown at src/Loader/FilesystemLoader.php:261

    {
        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];
        }

        return [$default, $name];
    }

    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));
            }
        }
    }

View on GitHub (pinned to a414c3a491)