symfony/http-kernel · error · LogicException
Cannot mix virtual and HTTP requests.
Error message
Cannot mix virtual and HTTP requests.
What it means
Symfony's VirtualRequestStack decorates the standard RequestStack to manage 'virtual' requests (marked with a _virtual_type attribute, used e.g. by internal sub-operations). Its push() throws LogicException if a virtual request is pushed while a real HTTP request is currently active — virtual and real requests cannot coexist on the stack. This enforces the invariant that the stack holds one consistent kind of request at a time.
Solutions
- Do not push virtual requests while a real request is on the stack — ensure $this->decorated->getCurrentRequest() is null before pushing.
- Complete (pop) the HTTP request before pushing virtual ones, or restructure to use sub-requests via HttpKernel::handle() with SUB_REQUEST type instead.
- Audit listeners/subscribers that call push() and remove or guard them when a master request exists.
- If migrating, replace virtual request usage with the modern RequestStack/fragment handling in current Symfony.
Example fix
// before
if ($event->isMasterRequest()) {
$this->virtualStack->push($virtualRequest);
}
// after
if (null === $this->requestStack->getCurrentRequest()) {
$this->virtualStack->push($virtualRequest);
} Defensive patterns
Strategy: try-catch
Validate before calling
if ($request->attributes->has('_virtual_type') && null !== $decoratedStack->getCurrentRequest()) {
throw new \RuntimeException('Cannot push a virtual request inside an active HTTP request.');
} Type guard
function canPushVirtual(\Symfony\Component\HttpFoundation\RequestStack $stack, \Symfony\Component\HttpFoundation\Request $r): bool
{
return !$r->attributes->has('_virtual_type') || null === $stack->getCurrentRequest();
} Try / catch
try {
$virtualStack->push($virtualRequest);
} catch (\LogicException $e) {
$logger->warning('Skipped virtual push: '.$e->getMessage());
return; // or handle via HttpKernel sub-request instead
} Prevention
- Never push virtual requests from listeners that run during a live HTTP request.
- Prefer HttpKernel::handle() with SUB_REQUEST over virtual-request tricks in modern Symfony.
- Guard all push() call sites with a getCurrentRequest() === null check when dealing with virtual types.
- Document which parts of your app operate outside the HTTP cycle and may legitimately use virtual requests.
When it happens
Trigger: Calling $stack->push($virtualRequest) while getCurrentRequest() returns a live HTTP request; mixing template/asset virtual request pushes inside a normal HTTP kernel handle() cycle; custom code (e.g. a listener) pushing a virtual request mid-HTTP-request.
Common situations: Integrating legacy VirtualRequestStack-based code (assets/templating-era Symfony) with modern HTTP-kernel flows; third-party bundles assuming virtual requests can be nested inside HTTP handling; custom error/fragment handlers pushing virtual requests.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Mapping variadic argument "$
- Could not resolve the "$
- The validation groups expression or closure must return a…
- Argument $ does not have a default value. Use "…
- Request payload contains invalid "form" data.
AI-assisted analysis of symfony/http-kernel@aa3a39d728 (2026-09-13).
Data as JSON: /api/errors/8f2c55b90487fa55.
Report an issue: GitHub.
Appendix: source
Thrown at Debug/VirtualRequestStack.php:35
/**
* A stack able to deal with virtual requests.
*
* @internal
*
* @author Jules Pietri <jules@heahprod.com>
*/
final class VirtualRequestStack extends RequestStack
{
public function __construct(
private readonly RequestStack $decorated,
) {
}
public function push(Request $request): void
{
if ($request->attributes->has('_virtual_type')) {
if ($this->decorated->getCurrentRequest()) {
throw new \LogicException('Cannot mix virtual and HTTP requests.');
}
parent::push($request);
return;
}
$this->decorated->push($request);
}
public function pop(): ?Request
{
return $this->decorated->pop() ?? parent::pop();
}
public function getCurrentRequest(): ?Request
{
return $this->decorated->getCurrentRequest() ?? parent::getCurrentRequest();View on GitHub (pinned to aa3a39d728)