symfony/http-kernel · error · RuntimeException
Unable to process an SSI tag without a "virtual" attribute.
Error message
Unable to process an SSI tag without a "virtual" attribute.
What it means
During SSI response processing (only for configured content types), the body is split on <!--#include ... --> comments and attributes parsed by regex instead of a real XML parser. When a matched include directive lacks the "virtual" attribute, no include target can be determined, so this validation guard fires. It indicates malformed SSI markup in the response — an SSI include directive written without virtual, or with unrecognized attribute syntax.
Solutions
- Add virtual="..." to the SSI include tag.
- Verify the tag uses SSI syntax (virtual) not ESI syntax (src).
- Remove the tag if the include is not needed.
Example fix
// before <!--#include set="box" --> // after <!--#include virtual="/_fragment/news" -->
Defensive patterns
Strategy: validation
Validate before calling
if (preg_match('/<!--#include((?![^>]*virtual=)[^>]*)-->/', $html)) { // fix SSI tags missing virtual } Prevention
- Remember SSI uses virtual, ESI uses src
- Lint templates when migrating between ESI and SSI
- Test rendered pages with SSI enabled
When it happens
Trigger: HTML containing <!--#include ...--> without virtual=, processed on a text/html response with SSI enabled.
Common situations: Migrating ESI tags to SSI (src vs virtual mismatch), or hand-written SSI comments missing the attribute.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Unable to process an ESI tag without a "src" attribute.
- Error when rendering
- Unable to create the store directory
- Passing non-scalar values as part of URI attributes to the…
- The " " renderer does not exist.
AI-assisted analysis of symfony/http-kernel@aa3a39d728 (2026-09-13).
Data as JSON: /api/errors/e61ed98c4b9e3e19.
Report an issue: GitHub.
Appendix: source
Thrown at HttpCache/Ssi.php:67
if (!\in_array($parts[0], $this->contentTypes, true)) {
return $response;
}
// we don't use a proper XML parser here as we can have SSI tags in a plain text response
$content = $response->getContent();
$boundary = self::generateBodyEvalBoundary();
$chunks = preg_split('#<!--\#include\s+(.*?)\s*-->#', $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
$i = 1;
while (isset($chunks[$i])) {
$options = [];
preg_match_all('/(virtual)="([^"]*?)"/', $chunks[$i], $matches, \PREG_SET_ORDER);
foreach ($matches as $set) {
$options[$set[1]] = $set[2];
}
if (!isset($options['virtual'])) {
throw new \RuntimeException('Unable to process an SSI tag without a "virtual" attribute.');
}
$chunks[$i] = $boundary.$options['virtual']."\n\n\n";
$i += 2;
}
$content = $boundary.implode('', $chunks).$boundary;
$response->setContent($content);
$response->headers->set('X-Body-Eval', 'SSI');
// remove SSI/1.0 from the Surrogate-Control header
$this->removeFromControl($response);
return $response;
}
}
View on GitHub (pinned to aa3a39d728)