twigphp/Twig · error · SyntaxError
Only "include" tags are allowed within a "sandbox" section.
Error message
Only "include" tags are allowed within a "sandbox" section.
What it means
A {% sandbox %} block may only contain {% include %} tags (whitespace text nodes are ignored). The sandbox exists to evaluate untrusted templates safely, and only includes of sandboxed templates are permitted; any other tag inside is a syntax error.
Solutions
- Move all logic into a separate template and inside the sandbox use only '{% include "untrusted.html.twig" %}'
- Ensure the included template is registered via sundry/security policy with SandboxExtension enabled
- Remove the sandbox wrapper if you don't actually need sandboxing of untrusted includes
Example fix
// before
{% sandbox %}{% set x = 1 %}{{ x }}{% endsandbox %}
// after
{% sandbox %}{% include 'untrusted.html.twig' %}{% endsandbox %} Defensive patterns
Strategy: validation
Validate before calling
// Only permit include tags inside sandbox blocks
if (preg_match('/\{%-?\s*sandbox\b.*?\{%-?\s*endsandbox/s', $src, $m) && preg_match('/\{%-?\s*(?!include\b|endsandbox)(\w+)/s', $m[0], $bad)) {
throw new InvalidArgumentException(sprintf('Tag "%s" is not allowed inside a sandbox block.', $bad[1]));
} Try / catch
try {
$twig->render($untrustedTemplate);
} catch (SyntaxError $e) {
// reject untrusted template: only include-in-sandbox templates are accepted
} Prevention
- Remember {% sandbox %} only wraps {% include %} calls, not arbitrary code
- Keep untrusted templates free of tags; put all logic in trusted wrapper templates
- Enable and configure Twig\Extension\SandboxExtension with a proper policy
When it happens
Trigger: Writing '{% sandbox %}{% for x in y %}...{% endfor %}{% endsandbox %}' or placing any tag other than include (e.g. 'set', 'if', raw text output) directly inside a sandbox section; SandboxTokenParser::parse throws when a body node is not an IncludeNode.
Common situations: Assuming {% sandbox %} sandboxes arbitrary template code (it only guards included templates); wrapping existing template snippets in sandbox and forgetting to move their logic into separate templates included via {% include %}.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- The callable passed to the
- The sandbox requires a strict security policy, call…
- The environment passed to
- An exception has been thrown during the compilation of a…
- Calling the "parent" function outside of a block is…
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/4130f48ff135e0a4.
Report an issue: GitHub.
Appendix: source
Thrown at src/TokenParser/SandboxTokenParser.php:53
{
$stream = $this->parser->getStream();
trigger_deprecation('twig/twig', '3.15', \sprintf('The "sandbox" tag is deprecated in "%s" at line %d.', $stream->getSourceContext()->getName(), $token->getLine()));
$stream->expect(Token::BLOCK_END_TYPE);
$body = $this->parser->subparse([$this, 'decideBlockEnd'], true);
$stream->expect(Token::BLOCK_END_TYPE);
// in a sandbox tag, only include tags are allowed
if ($body instanceof IncludeNode) {
$body->setAttribute('sandboxed', true);
} else {
foreach ($body as $node) {
if ($node instanceof TextNode && ctype_space($node->getAttribute('data'))) {
continue;
}
if (!$node instanceof IncludeNode) {
throw new SyntaxError('Only "include" tags are allowed within a "sandbox" section.', $node->getTemplateLine(), $stream->getSourceContext());
}
$node->setAttribute('sandboxed', true);
}
}
return new SandboxNode($body, $token->getLine());
}
public function decideBlockEnd(Token $token): bool
{
return $token->test('endsandbox');
}
public function getTag(): string
{
return 'sandbox';
}View on GitHub (pinned to a414c3a491)