symfony/http-kernel · error · RuntimeException

Error when rendering

Error message

Error when rendering "%s" (Status code is %d).

What it means

AbstractSurrogate::handle() throws RuntimeException when the surrogate sub-request response is neither successful nor 304 Not Modified. Unlike the FragmentHandler version, no alt/ignoreErrors logic applies before the throw; the exception is caught internally only to try the alt URI.

Solutions

  1. Fix the src URL of the ESI/SSI tag so it returns 200/304.
  2. Add an alt attribute so the surrogate falls back to an alternate resource on failure.
  3. Add onerror="continue" to the tag to ignore failures for that include.

Example fix

// before
<esi:include src="/_fragment/broken" />
// after
<esi:include src="/_fragment/broken" alt="/_fragment/fallback" onerror="continue" />
Defensive patterns

Strategy: fallback

Validate before calling

// pre-check upstream URL availability if cheap: HEAD request returning 2xx/304

Try / catch

try { $content = $surrogate->handle($cache, $uri, $alt, false); } catch (\RuntimeException $e) { $content = $renderFallback(); }

Prevention

When it happens

Trigger: An ESI/SSI include target URL returns 404/500 etc.; render() of the surrogate receives a failed Response for the embedded resource.

Common situations: Broken src attribute pointing to a removed route; upstream page erroring; permission changes making an include inaccessible.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of symfony/http-kernel@aa3a39d728 (2026-09-13). Data as JSON: /api/errors/d8d3084ae4c0bed3. Report an issue: GitHub.

Appendix: source

Thrown at HttpCache/AbstractSurrogate.php:79

    {
        if (!$control = $response->headers->get('Surrogate-Control')) {
            return false;
        }

        $pattern = \sprintf('#content="[^"]*%s/1.0[^"]*"#', strtoupper($this->getName()));

        return (bool) preg_match($pattern, $control);
    }

    public function handle(HttpCache $cache, string $uri, string $alt, bool $ignoreErrors): string
    {
        $subRequest = Request::create($uri, 'GET', [], $cache->getRequest()->cookies->all(), [], $cache->getRequest()->server->all());

        try {
            $response = $cache->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true);

            if (!$response->isSuccessful() && Response::HTTP_NOT_MODIFIED !== $response->getStatusCode()) {
                throw new \RuntimeException(\sprintf('Error when rendering "%s" (Status code is %d).', $subRequest->getUri(), $response->getStatusCode()));
            }

            return $response->getContent();
        } catch (\Exception $e) {
            if ($alt) {
                return $this->handle($cache, $alt, '', $ignoreErrors);
            }

            if (!$ignoreErrors) {
                throw $e;
            }
        }

        return '';
    }

    /**
     * Remove the Surrogate from the Surrogate-Control header.

View on GitHub (pinned to aa3a39d728)