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

  1. Do not push virtual requests while a real request is on the stack — ensure $this->decorated->getCurrentRequest() is null before pushing.
  2. Complete (pop) the HTTP request before pushing virtual ones, or restructure to use sub-requests via HttpKernel::handle() with SUB_REQUEST type instead.
  3. Audit listeners/subscribers that call push() and remove or guard them when a master request exists.
  4. 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

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


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)