Skip to content

fix(symfony): clean up sub-request scope and record exceptions on spans#662

Open
PuvaanRaaj wants to merge 1 commit into
open-telemetry:mainfrom
PuvaanRaaj:fix/1844-symfony-subrequest
Open

fix(symfony): clean up sub-request scope and record exceptions on spans#662
PuvaanRaaj wants to merge 1 commit into
open-telemetry:mainfrom
PuvaanRaaj:fix/1844-symfony-subrequest

Conversation

@PuvaanRaaj

Copy link
Copy Markdown
Contributor

Summary

Fixes two related bugs in the Symfony auto-instrumentation HttpKernel::handle() hook lifecycle, both rooted in the same sub-request handling code path.

Root cause

Symfony calls HttpKernel::handle() twice when it renders an error response internally: once for the MAIN_REQUEST and again as a nested SUB_REQUEST dispatched to the error controller. The old handle post-hook returned early whenever there was no exception:

$scope = Context::storage()->scope();
if (null === $scope || null === $exception) {
    return;
}

The error-controller sub-request renders successfully (no exception), so its span was never ended and its scope was never detached. This left the sub-request scope leaked on top of the context stack.

  • #1905 — With the sub-request scope leaked, terminate() popped only that topmost (sub-request) scope. The main-request root SERVER span was never popped, ended, or exported, so child spans showed rootServiceName: <root span not yet received>.
  • #1844handleThrowable records the exception on the currently-active span (the main span) before the error sub-request is dispatched. Because the leaked sub-request scope displaced the main scope, terminate() ended the wrong span and the main span — the one carrying the exception event and Status: Error — was never exported.

The fix

In the handle post-hook:

  • A MAIN_REQUEST that returned normally still returns early and is ended by the terminate hook (unchanged behaviour).
  • Every other path — sub-requests, and any request where an exception propagated out of handle() — now detaches its scope and ends its span in the post-hook, since neither is ever passed to terminate(). This ensures every attached scope is detached exactly once and no scope leaks onto the context stack.
  • Exceptions continue to be recorded (recordException + STATUS_ERROR) on the active span when present.

With the sub-request scope cleaned up immediately, terminate() sees the main-request scope again and ends/exports the root span with its exception event intact.

Testing

  • Added test_http_kernel_sub_request_scope_does_not_leak_main_span: a main request followed by an internal sub-request leaves no leaked scope; the sub-request span exports immediately and the main root SERVER span is ended and exported by terminate().
  • Added test_http_kernel_records_exception_on_main_span_during_sub_request_error_handling: an exception recorded on the active span during error handling ends up on the exported main span.
  • Updated the existing sub-request tests to stop calling terminate() after a SUB_REQUEST handle (Symfony never does this for sub-requests; the span is now ended by the post-hook).
  • Full suite passes locally: vendor/bin/phpunit → 24 tests, 132 assertions, OK. php-cs-fixer --dry-run clean.

Fixes open-telemetry/opentelemetry-php#1905
Fixes open-telemetry/opentelemetry-php#1844

@PuvaanRaaj
PuvaanRaaj requested a review from a team as a code owner July 18, 2026 08:05
@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.77%. Comparing base (176b3fc) to head (29dde05).

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##               main     #662      +/-   ##
============================================
- Coverage     81.69%   80.77%   -0.92%     
+ Complexity     1821     1448     -373     
============================================
  Files           129       95      -34     
  Lines          7144     5347    -1797     
============================================
- Hits           5836     4319    -1517     
+ Misses         1308     1028     -280     
Flag Coverage Δ
Context/Swoole 0.00% <ø> (ø)
Exporter/Instana 49.80% <ø> (ø)
Instrumentation/AwsSdk 82.14% <ø> (ø)
Instrumentation/CakePHP 20.42% <ø> (ø)
Instrumentation/CodeIgniter 79.31% <ø> (ø)
Instrumentation/Curl 86.88% <ø> (ø)
Instrumentation/Doctrine 92.82% <ø> (ø)
Instrumentation/ExtAmqp 88.80% <ø> (ø)
Instrumentation/ExtRdKafka ?
Instrumentation/Guzzle 79.76% <ø> (ø)
Instrumentation/HttpAsyncClient 78.94% <ø> (ø)
Instrumentation/HttpConfig 28.76% <ø> (ø)
Instrumentation/IO 0.00% <ø> (ø)
Instrumentation/Laravel ?
Instrumentation/Magento2 88.12% <ø> (ø)
Instrumentation/MongoDB 76.84% <ø> (ø)
Instrumentation/MySqli ?
Instrumentation/OpenAIPHP 86.71% <ø> (ø)
Instrumentation/PostgreSql 91.36% <ø> (ø)
Instrumentation/Psr14 77.41% <ø> (ø)
Instrumentation/Psr15 89.74% <ø> (ø)
Instrumentation/Psr16 97.43% <ø> (ø)
Instrumentation/Psr18 79.41% <ø> (ø)
Instrumentation/Psr6 97.56% <ø> (ø)
Instrumentation/ReactPHP 99.41% <ø> (ø)
Instrumentation/Session 94.28% <ø> (ø)
Instrumentation/Slim 84.21% <ø> (ø)
Metrics/Runtime ?
Propagation/CloudTrace 90.69% <ø> (ø)
Propagation/Instana 98.07% <ø> (ø)
Propagation/ServerTiming 94.73% <ø> (ø)
Propagation/TraceResponse 94.73% <ø> (ø)
ResourceDetectors/Azure 91.66% <ø> (ø)
ResourceDetectors/DigitalOcean 100.00% <ø> (ø)
Sampler/Xray 78.38% <ø> (ø)
Shims/OpenTracing 92.99% <ø> (ø)
SqlCommenter 95.58% <ø> (ø)
Utils/Test 87.79% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.
see 34 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 176b3fc...29dde05. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1b6543922d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +117 to +118
if ($isSubRequest && null !== $response) {
$span->setAttribute(TraceAttributes::HTTP_RESPONSE_STATUS_CODE, $response->getStatusCode());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark 5xx sub-request spans as errors

When a Symfony error-controller sub-request returns a 500 response without throwing, this new post-hook path is now the only place that completes that sub-request span, but it only records http.response.status_code before ending it. The terminate() path still marks 5xx responses with STATUS_ERROR, so moving sub-request completion here causes exported 500 error-controller spans to have an unset status and lose the error signal.

Useful? React with 👍 / 👎.

Detach the scope attached by the kernel handle hook on every request type so main-request root spans end and export (#1905); record exceptions surfaced during (sub-)request handling on the active span (#1844).

Also resolves pre-existing Symfony psalm CI failures in the files touched here: add the missing @psalm-suppress UnusedFunctionCall on the terminate() hook, and add open-telemetry/opentelemetry-propagation-traceresponse to require-dev (plus the use import) so SymfonyInstrumentationTest can resolve TraceResponsePropagator.

Fixes open-telemetry/opentelemetry-php#1905 and open-telemetry/opentelemetry-php#1844.

Risk-Level: medium
AI-Agent: claude-opus-4-8
@PuvaanRaaj
PuvaanRaaj force-pushed the fix/1844-symfony-subrequest branch from 1b65439 to 29dde05 Compare July 18, 2026 09:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant