Skip to content

Preserve cancellation across AI, MCP and telemetry integrations - #98

Merged
binaryfire merged 27 commits into
0.4from
fix/cancellation-integrations
Nov 2, 2026
Merged

binaryfire merged 27 commits into
0.4from
fix/cancellation-integrations

Conversation

@binaryfire

@binaryfire binaryfire commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Cancellation could be mistaken for an ordinary failure when AI callbacks, MCP transports or telemetry hooks threw during cleanup. That could trigger retries, send error responses or continue recording after the operation had been canceled.

Preserve cancellation at the existing integration boundaries:

  • Keep AI failure observers and conversation persistence from replacing cancellation; preserve child-task ownership and completed-turn fallback behavior.
  • Stop MCP handshake retries and server error responses when cancellation is wrapped by cleanup, while still closing owned transports.
  • Propagate cancellation from OpenTelemetry, Sentry and Telescope callbacks without hiding genuine ordinary failures or skipping required cleanup.

Regression coverage checks direct and wrapped cancellation, callback failures, transport cleanup and ordinary-error behavior. Targeted source analysis and affected tests pass. The full suite and service matrices run in CI.


Summary by cubic

Cancellation was previously mistaken for an ordinary failure when AI callbacks, MCP transports, or telemetry hooks threw during cleanup. That could trigger retries, send error responses, or continue recording after the operation had been canceled.

Cancellation is now preserved at integration boundaries by using Coroutine::causedByCancellation and preferCancellation instead of referring to Swoole\Coroutine\CanceledException directly.

  • AI failure observers, step/tool reporters, and stream catch callbacks now run from finally, keeping the original cancellation alive even when reporting throws. Independently canceled concurrent tools and title generation surface as ChildCancellationException without discarding completed turns.
  • MCP handshake fallback no longer retries after cancellation, server handlers no longer convert cancellation into error responses or empty completions, and transport cleanup prefers cancellation while still closing owned sessions.
  • OpenTelemetry, Sentry, and Telescope fallback catches now rethrow cancellation chains before applying their ordinary-error handling, so canceled operations stop telemetry recording exactly where they should.

Regression tests cover direct and wrapped cancellation, throwing callbacks, transport cleanup, and ordinary-error behavior across the affected integrations.

Written for commit 9f7e35e. Summary will update on new commits.

View guided diff Turn on auto-fix

Note

Preserve cancellation across AI, MCP and telemetry integrations

  • Replaces direct CanceledException type checks with Coroutine::causedByCancellation and Coroutine::preferCancellation across OpenTelemetry, Sentry, Telescope and MCP instrumentation, so cancellations wrapped in other exceptions propagate instead of being logged or converted to fallback values
  • Reworks AI failure-reporting paths (RememberConversation, GeneratesText, StreamsText, TextGenerationLoop, InvokesTools, Provider.dispatchingFailure, StreamableAgentResponse) to throw the original exception first and run reporting in finally, so a reporting failure retains the original as its previous exception
  • Cancellation-caused tool failures and title-generation failures are now stored as ChildCancellationException with the caught exception as previous
  • MCP Server.sendException and response handling now rethrow cancellation-caused exceptions instead of converting them to JSON-RPC errors; ClientManager and transport cleanup paths give cancellation precedence over other failures
  • Risk: behavioral change — places that previously swallowed or logged failures (e.g. JsonNormalizer.normalize, DumpWatcher::shouldRecordDump, Telescope Auth/cache lookups, Hub::startTransaction samplers) now rethrow any throwable whose cause chain contains a cancellation; non-cancellation failures keep prior behavior

Macroscope summarized 9f7e35e.

Summary by CodeRabbit

  • Bug Fixes
    • Coroutine cancellation is now preserved across AI generation, tool execution, conversation storage, MCP requests, and telemetry integrations—even when wrapped by another exception.
    • Failure reporting and cleanup are less likely to obscure the original error or cancellation.
    • Completed conversation turns remain saved when title generation is canceled, and canceled operations are no longer silently treated as ordinary failures.
  • Tests
    • Expanded coverage for direct and wrapped cancellations, cleanup failures, and exception handling.

A failure-event factory or listener could replace an operation's cancellation with an ordinary exception, allowing retry() to run the canceled operation again.

Dispatch failure events from finally while the original exception is in flight. PHP retains that original cause if event construction or dispatch fails, without changing successful operations or optional-listener checks.

Validation: OperationEventsTest passes all 15 tests, including a throwing failure observer that must not cause a retry. Targeted source analysis and formatting pass.
Throwing agent, step or tool failure observers could erase the original cancellation. Separately, cancellation of one concurrent tool was exposed as cancellation of the still-active caller.

Run failure reporting from finally to retain the original exception chain. Mark independently canceled tool failures with the existing ChildCancellationException boundary, preserving sibling cancellation and joining before the caller receives the failure.

Validation: seven focused AgentEvents cases and all nine ConcurrentTools tests pass. Coverage includes prompt and stream failure reporting, middleware after generation, tool observers, ordinary tool failures and direct or wrapped child cancellation. Targeted source analysis and formatting pass.
A public stream catch callback that throws could hide cancellation from the failed producer, making an interrupted stream look like an ordinary failure.

Invoke the already-cleared callback list from finally while the producer exception remains in flight. Preserve callback execution context and once-only handling while retaining both failures through PHP's exception chain.

Validation: StreamableAgentResponseTest passes all nine tests, including throwing catch callbacks and repeated stream iteration. Targeted source analysis and formatting pass.
Conversation persistence failures could replace cancellation during generation or title work. Independently canceled title generation could also discard a completed caller turn.

Preserve cancellation through failed-turn recording and completed-turn persistence using finally. Mark independent title-child cancellation with ChildCancellationException and use the existing fallback title so the completed turn is stored. Cancellation of the caller still propagates after owned persistence.

Validation: 13 focused RememberConversation cases pass on SQLite, covering direct and wrapped title-child cancellation, failed-turn storage, caller title cancellation, and title cleanup. Targeted source analysis and formatting pass.
When an oversized remote attachment also failed during cancellation cleanup, the size-limit exception could replace the cancellation chain.

Use the shared cancellation predicate before replacing a transfer failure with the existing size-limit message. Ordinary oversized responses keep their current behavior; canceled transfers retain their original failure.

Validation: both direct and wrapped cancellation cases pass against the bounded loopback transfer fixture. Targeted source analysis and formatting pass.
MCP handlers and generators could convert cancellation carrying a cleanup failure into a protocol error, tool error or empty completion. Debug-mode rethrows concealed the server failure in tests.

Check cancellation at the shared server error boundary and the response-conversion and completion fallbacks. Rethrow the original failure while preserving ordinary error responses and the completion catch's original Exception breadth.

Validation: ServerTest passes 52 tests, CallToolTest 24 and CompletionCompleteTest 26. Regressions cover debug-disabled server handling, immediate and streamed tool failures, and direct or wrapped completion cancellation. Targeted source analysis and formatting pass.
A remembered legacy handshake could retry after wrapped cancellation, while fallback initialization could relabel cancellation as an additional handshake failure. Cleanup precedence also recognized only bare cancellation.

Use the existing cancellation predicate before handshake fallback or failure wrapping and preferCancellation when cleanup throws. Keep authorization errors, ordinary negotiation fallback and original exception identity unchanged.

Validation: HandshakeTest passes all 35 tests, including remembered-handshake cancellation, discovery timeout followed by cancellation, and cancellation during disconnect. Targeted source analysis and formatting pass.
Managed client teardown could suppress a ClientException carrying cancellation or retain an earlier ordinary failure instead of later cancellation.

Preserve cancellation at the client-error suppression boundary and use the shared precedence helper while still visiting every owned client. Ordinary client disconnect failures remain ignored as before.

Validation: ClientManagerTest passes all 38 tests, including direct, wrapped and transport-wrapped cancellation and verification that every client is visited once. Targeted source analysis and formatting pass.
Session termination treated cleanup-wrapped cancellation as an ignorable DELETE failure, and response cleanup selected cancellation only by its outer exception type.

Use the shared predicate for termination failures and the existing precedence helper for owned exchange cleanup. Session state is still reset and owned responses closed; active sibling consumers retain their transfer ownership.

Validation: HttpTransportTest passes all 45 tests, including direct and wrapped termination cancellation, response closure and idempotent disconnect. Targeted source analysis and formatting pass.
A failed stdio receive could hide cancellation raised through process-stop cleanup because teardown checked only the outer exception type.

Use preferCancellation when disconnect cleanup fails. Preserve the original primary failure for ordinary cleanup errors and retain existing process/input detachment before propagating cancellation.

Validation: three focused StdioTransport cases pass for direct disconnect, wrapped disconnect and wrapped cleanup after receive failure. Tests use a bounded process mock; targeted source analysis and formatting pass.
A cache-key resolver's cleanup failure could hide cancellation and allow completion telemetry to continue.

Use the shared cancellation predicate before logging an ordinary resolver error and omitting the key. Preserve ordinary resolver isolation and the existing metrics and tracing behavior.

Validation: CacheInstrumentationTest passes all eight tests, including direct and wrapped cancellation with no completion telemetry. Targeted source analysis and formatting pass.
Exception enrichment could continue through later callbacks after an enricher threw a cleanup error carrying cancellation.

Rethrow cancellation chains before logging and continuing past ordinary enricher failures. The original failure remains intact and later enrichers are not invoked for canceled work.

Validation: ExceptionInstrumentationTest passes all 12 tests. Regression cases cover direct and wrapped cancellation, later-callback suppression and no emitted record. Targeted source analysis and formatting pass.
A URL-template resolver could swallow cleanup-wrapped cancellation and proceed with a physical HTTP request.

Use the cancellation predicate at the resolver fallback boundary. Keep terminal error reporting unchanged so a genuine cleanup error remains observable while cancellation stops the request.

Validation: HttpClientInstrumentationTest passes all 13 tests. Both cancellation forms prevent the physical send; the wrapped cleanup failure retains its error span and duration metric. Targeted source analysis and formatting pass.
Redis query-text resolution could hide cancellation behind a cleanup error and emit misleading completion telemetry.

Recognize cancellation chains before applying the existing ordinary-error logging and omitted-query fallback. Leave normal command telemetry and resolver error isolation unchanged.

Validation: RedisInstrumentationTest passes all 11 tests, including direct and wrapped resolver cancellation with no completion telemetry. Targeted source analysis and formatting pass.
A user-context resolver's cleanup failure could be treated as an ordinary lookup error even when the operation was canceled.

Use the shared predicate before recording a resolver failure. Propagate the original cancellation chain and retain the existing ordinary-error fallback.

Validation: both UserContextResolverTest cases pass, exercising direct and wrapped cancellation from the configured resolver. Targeted source analysis and formatting pass.
Breadcrumb and sampling callbacks could replace cancellation with a cleanup failure, causing the Hub to log the error and continue.

Use the cancellation predicate before the existing callback-error fallback in breadcrumb, trace-sampler and profile-sampler handling. Keep configured sampling fallback and ordinary callback error logging unchanged.

Validation: HubTest passes all 14 tests with explicit direct and wrapped cancellation rows for each callback. Targeted source analysis and formatting pass.
Application accessors invoked by Sentry event handling can perform cancellable work. A cleanup exception carrying cancellation was ignored as an ordinary instrumentation failure.

Use the shared predicate at the event-dispatch catch, preserving ordinary failure isolation and the original cancellation exception chain.

Validation: AuthEventsTest passes all 12 tests, including direct and wrapped cancellation through a user email accessor. Targeted source analysis and formatting pass.
Tracing event dispatch ignored cleanup failures containing cancellation and could continue an interrupted operation.

Recognize cancellation chains at the existing event-handler catch. Ordinary instrumentation errors remain isolated from application execution.

Validation: tracing EventHandlerTest passes all nine tests, including direct and wrapped cancellation and ordinary instrumentation failure handling. Targeted source analysis and formatting pass.
User-defined tool schemas may perform cancellable work. Cleanup-wrapped cancellation was treated as an ignorable schema resolution failure.

Use the shared cancellation predicate before retaining the existing ordinary schema-error fallback. No schema invocation or successful tracing behavior changes.

Validation: the two focused AiIntegration cases pass, verifying that ordinary schema failures remain isolated while wrapped cancellation escapes tracing. Targeted source analysis and formatting pass.
Session-store resolution during cache tracing caught every failure, including cancellation raised by application bindings or cleanup.

Rethrow cancellation chains before the existing missing-session fallback. Preserve the reentry guard's cleanup and ordinary command tracing when session resolution fails normally.

Validation: both focused CacheIntegration cases pass for ordinary resolution failure and wrapped cancellation. Targeted source analysis and formatting pass.
An application serializer's cancellation could be replaced with Telescope's purged-value fallback.

Check the exception chain before purging serialization failures. Preserve ordinary malformed-value handling while allowing canceled application work to stop.

Validation: ExtractPropertiesTest passes all five tests, including a throwing serializer carrying cancellation and the existing ordinary-failure purge behavior. Targeted source analysis and formatting pass.
Loading monitored tags could hide cancellation and replace the existing tags with an empty list.

Rethrow cancellation before applying the ordinary storage-error fallback. Preserve existing tags when the load is interrupted and keep the empty fallback for ordinary failures.

Validation: the two cancellation/fallback rows and neighboring unique-tag storage test pass on SQLite. Targeted source analysis and formatting pass.
Recording-state cache lookups and application user accessors could swallow cancellation, enabling recording or queuing entries after the operation had stopped.

Recognize cancellation chains at both existing fallback catches. Preserve ordinary lookup-error behavior and the coroutine-local recording state.

Validation: six focused SQLite cases pass, covering pause lookup, user resolution, later recording after callback failures and command-start recording. Targeted source analysis and formatting pass.
Telescope could turn cancellation from a request or response body producer into a purged payload and continue recording.

Rethrow cancellation chains before the existing payload-error fallback. Preserve owned stream-position restoration and ordinary unreadable-payload handling.

Validation: request-body and cached-response cancellation cases and the neighboring raw payload test pass on SQLite. Targeted source analysis and formatting pass.
A canceled cache lookup for the dump-watcher flag was treated as an unavailable flag and delegated the dump as normal work.

Use the shared cancellation predicate before returning the existing false fallback. Ordinary cache failures still delegate; cancellation propagates without recording or delegation.

Validation: both focused DumpWatcher fallback cases pass on SQLite. The complete file also passed at the integration checkpoint. Targeted source analysis and formatting pass.
The requested audit of cancellation-sensitive callback, fallback and I/O catches is complete across the framework and integration groups.

Remove its completed task while retaining the separate proposal to investigate a focused PHPStan rule. The owner-approved Google SDK cause-loss limitation remains represented by its skipped regression and separate handoff.

Validation: checked the remaining todo entries and the final reviewed audit scope.
@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between d710ed1 and 9f7e35e.

Files selected for processing: 60
  • docs/todo.md
  • src/ai/src/Files/UntrustedUrl.php
  • src/ai/src/Gateway/Concerns/InvokesTools.php
  • src/ai/src/Gateway/TextGenerationLoop.php
  • src/ai/src/Middleware/RememberConversation.php
  • src/ai/src/Providers/Concerns/GeneratesText.php
  • src/ai/src/Providers/Concerns/StreamsText.php
  • src/ai/src/Providers/Provider.php
  • src/ai/src/Responses/StreamableAgentResponse.php
  • src/ai/src/Support/PendingConversationTitle.php
  • src/mcp/src/Client/ClientManager.php
  • src/mcp/src/Client/Protocol.php
  • src/mcp/src/Client/Transport/HttpTransport.php
  • src/mcp/src/Client/Transport/StdioTransport.php
  • src/mcp/src/Server.php
  • src/mcp/src/Server/Methods/CompletionComplete.php
  • src/mcp/src/Server/Methods/Concerns/InteractsWithResponses.php
  • src/opentelemetry/src/Instrumentation/CacheInstrumentation.php
  • src/opentelemetry/src/Instrumentation/ExceptionInstrumentation.php
  • src/opentelemetry/src/Instrumentation/HttpClientInstrumentation.php
  • src/opentelemetry/src/Instrumentation/RedisInstrumentation.php
  • src/opentelemetry/src/Support/UserContextResolver.php
  • src/sentry/src/EventHandler.php
  • src/sentry/src/Features/AiIntegration.php
  • src/sentry/src/Features/CacheIntegration.php
  • src/sentry/src/Hub.php
  • src/sentry/src/Tracing/EventHandler.php
  • src/telescope/src/JsonNormalizer.php
  • src/telescope/src/Storage/DatabaseEntriesRepository.php
  • src/telescope/src/Telescope.php
  • src/telescope/src/Watchers/ClientRequestWatcher.php
  • src/telescope/src/Watchers/DumpWatcher.php
  • tests/Ai/ConcurrentToolsTest.php
  • tests/Ai/Feature/AgentEventsTest.php
  • tests/Ai/Feature/Files/UntrustedUrlTest.php
  • tests/Ai/Feature/OperationEventsTest.php
  • tests/Ai/Feature/RememberConversationTest.php
  • tests/Ai/Unit/Responses/StreamableAgentResponseTest.php
  • tests/Mcp/Unit/Client/ClientManagerTest.php
  • tests/Mcp/Unit/Client/HandshakeTest.php
  • tests/Mcp/Unit/Client/Transport/HttpTransportTest.php
  • tests/Mcp/Unit/Client/Transport/StdioTransportTest.php
  • tests/Mcp/Unit/Methods/CallToolTest.php
  • tests/Mcp/Unit/Methods/CompletionCompleteTest.php
  • tests/Mcp/Unit/ServerTest.php
  • tests/OpenTelemetry/Instrumentation/CacheInstrumentationTest.php
  • tests/OpenTelemetry/Instrumentation/ExceptionInstrumentationTest.php
  • tests/OpenTelemetry/Instrumentation/HttpClientInstrumentationTest.php
  • tests/OpenTelemetry/Instrumentation/RedisInstrumentationTest.php
  • tests/OpenTelemetry/Support/UserContextResolverTest.php
  • tests/Sentry/EventHandler/AuthEventsTest.php
  • tests/Sentry/Features/AiIntegrationTest.php
  • tests/Sentry/Features/CacheIntegrationTest.php
  • tests/Sentry/HubTest.php
  • tests/Sentry/Tracing/EventHandlerTest.php
  • tests/Telescope/ExtractPropertiesTest.php
  • tests/Telescope/Storage/DatabaseEntriesRepositoryTest.php
  • tests/Telescope/Telescope/TelescopeTest.php
  • tests/Telescope/Watchers/ClientRequestWatcherTest.php
  • tests/Telescope/Watchers/DumpWatcherTest.php
Files with no reviewable changes: 1
  • docs/todo.md

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The changes extend coroutine cancellation detection across AI, MCP, OpenTelemetry, Sentry, and Telescope. Failure callbacks and cleanup paths preserve cancellation-related exceptions, including wrapped cancellation. AI conversation handling also persists completed turns during independent title cancellation.

Changes: Coroutine cancellation handling

Layer / File(s) Summary
AI failure reporting and cancellation
docs/todo.md, src/ai/src/Files/*, src/ai/src/Gateway/*, src/ai/src/Providers/*, src/ai/src/Responses/*, tests/Ai/ConcurrentToolsTest.php, tests/Ai/Feature/AgentEventsTest.php, tests/Ai/Feature/Files/*, tests/Ai/Feature/OperationEventsTest.php, tests/Ai/Unit/Responses/*
AI failure callbacks now run while the original exception is rethrown. Concurrent tool cancellation is wrapped as a child cancellation. Cancellation checks cover wrapped exceptions, including when a URL download reaches its size limit.
Conversation cancellation and persistence
src/ai/src/Middleware/RememberConversation.php, src/ai/src/Support/PendingConversationTitle.php, tests/Ai/Feature/RememberConversationTest.php
Conversation failure recording preserves the active exception. Title cancellation is handled separately, and persistence plus response updates run in a finally block. Tests cover title cancellation and storage failures.
MCP cancellation and cleanup
src/mcp/src/Client/*, src/mcp/src/Client/Transport/*, src/mcp/src/Server.php, src/mcp/src/Server/Methods/*, tests/Mcp/Unit/Client/*, tests/Mcp/Unit/Methods/*, tests/Mcp/Unit/ServerTest.php
MCP client, transport, and server paths use coroutine cancellation checks and exception-precedence helpers. Tests cover wrapped cancellation during initialization, requests, teardown, and response handling.
OpenTelemetry and Sentry cancellation
src/opentelemetry/src/Instrumentation/*, src/opentelemetry/src/Support/*, src/sentry/src/*, tests/OpenTelemetry/*, tests/Sentry/*
Instrumentation and event handlers rethrow cancellation-caused exceptions while retaining existing handling for other failures. Tests cover direct and wrapped cancellation and selected telemetry behavior.
Telescope cancellation handling
src/telescope/src/*, tests/Telescope/*
Serialization, storage, recording, and watcher paths rethrow cancellation-caused exceptions while retaining existing fallback behavior for other failures. Tests cover wrapped cancellation in these paths.

Estimated code review effort: 4 (Complex) | ~60 minutes

Merge Risk: Minimal · up to 9f7e3

Caller cancellation remains preserved during title generation; no identified issue needs resolution before merging.

Pre-merge checks: Passed 3 · Failed 1 · Inconclusive 1

Failed Failed checks: 1 warning · 1 inconclusive

Check name Status Explanation Resolution
Description check Warning The description explains the problem, intended behavior, affected areas, and regression coverage. However, it omits the selected contribution type, linked issue or discussion, concrete failing and pas… Select the applicable contribution type, link the relevant issue or discussion, identify the regression tests with their results before and after the fix, list the exact verification commands and results including the required composer fix …
Docstring Coverage Inconclusive Docstring coverage is 63.51% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: preserving cancellation across the affected AI, MCP, and telemetry integrations.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description explains the problem, intended behavior, affected areas, and regression coverage. However, it omits the selected contribution type, linked issue or discussion, concrete failing and passing regression-test results, verification commands and results, and completed submission checklist items.

Resolution

Select the applicable contribution type, link the relevant issue or discussion, identify the regression tests with their results before and after the fix, list the exact verification commands and results including the required composer fix run, and complete the before-submitting checklist.

Full details: Docstring Coverage

Explanation

Docstring coverage is 63.51% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 74 functions across 50 files. (9 skipped: 9 over the file limit.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

PHPMD (3.0.0)
src/mcp/src/Client/Transport/HttpTransport.php

PHPMD could not process this file (exit code 3): Unexpected token: &, line: 54, col: 9, file: /src/mcp/src/Client/Transport/HttpTransport.php.
#0 phar:///usr/bin/phpmd/vendor/pdepend/pdepend/src/Source/Language/PHP/AbstractPHPParser.php(8981): PDepend\Source\Parser\UnexpectedTokenException->__construct()
#1 phar:///usr/bin/phpmd/vendor/pdepend/pdepend/src/Source/Language/PHP/AbstractPHPParser.php(8968): PDepend\Source\Language\PHP\AbstractPHPParser->getUnexpectedTokenExcepti...

src/mcp/src/Client/ClientManager.php

PHPMD could not process this file (exit code 3): Unexpected token: &, line: 33, col: 9, file: /src/mcp/src/Client/ClientManager.php.
#0 phar:///usr/bin/phpmd/vendor/pdepend/pdepend/src/Source/Language/PHP/AbstractPHPParser.php(8981): PDepend\Source\Parser\UnexpectedTokenException->__construct()
#1 phar:///usr/bin/phpmd/vendor/pdepend/pdepend/src/Source/Language/PHP/AbstractPHPParser.php(8968): PDepend\Source\Language\PHP\AbstractPHPParser->getUnexpectedTokenException()
#2 ph...

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@binaryfire

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Preserve cancellation across AI, MCP, and telemetry integrations

🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Preserve cancellation through AI failure observers, conversation storage, and independently
 canceled child tasks.
• Prevent MCP retries and error responses after cancellation while retaining transport cleanup.
• Propagate cancellation from telemetry callbacks without changing ordinary-error fallbacks.
Diagram

graph TD
  A["Integration operation"] --> B["Boundary catches"] --> C{"Cancellation cause?"}
  C -->|Yes| D["Propagate failure"]
  C -->|No| E["Ordinary fallback"]
  B --> F["AI observers"] --> D
  B --> G["MCP cleanup"] --> D
  B --> H["Telemetry hooks"] --> C
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Centralize integration exception handling
  • ➕ Could reduce repeated cancellation checks across integrations.
  • ➖ Would couple distinct fallback, cleanup, and child-ownership policies; risks changing ordinary-error behavior.

Recommendation: Keep the localized boundary fixes. They reuse the existing Coroutine cancellation helpers while preserving each integration's cleanup and ordinary-failure contract; a shared wrapper would obscure those differences.

Files changed (60) +1070 / -257

Bug fix (31) +268 / -171
UntrustedUrl.phpKeep transfer cancellation ahead of file-size errors +2/-2

Keep transfer cancellation ahead of file-size errors

• Recognizes cancellation wrapped by transfer cleanup so an oversized-download error cannot replace it.

src/ai/src/Files/UntrustedUrl.php

InvokesTools.phpRetain tool cancellation during failure reporting +6/-3

Retain tool cancellation during failure reporting

• Runs tool-failure reporting while the original exception is in flight, retaining it as the cause if an observer throws.

src/ai/src/Gateway/Concerns/InvokesTools.php

TextGenerationLoop.phpPreserve generation failures and concurrent-tool ownership +28/-13

Preserve generation failures and concurrent-tool ownership

• Retains the original cause when step or tool failure observers throw. Marks cancellation originating in an independently running tool as child-owned before reporting it to the active caller.

src/ai/src/Gateway/TextGenerationLoop.php

RememberConversation.phpPreserve cancellation and completed conversation turns +56/-44

Preserve cancellation and completed conversation turns

• Keeps cancellation in the exception chain when failed-turn recording or storage fails. An independently canceled title falls back to a prompt-derived title without discarding the completed turn.

src/ai/src/Middleware/RememberConversation.php

GeneratesText.phpRetain prompt cancellation through agent failure events +6/-3

Retain prompt cancellation through agent failure events

• Reports agent failure without letting a throwing observer erase the cancellation that caused generation to fail.

src/ai/src/Providers/Concerns/GeneratesText.php

StreamsText.phpRetain stream cancellation through agent failure events +6/-3

Retain stream cancellation through agent failure events

• Keeps the original stream failure as the cause if agent-failure reporting throws.

src/ai/src/Providers/Concerns/StreamsText.php

Provider.phpRetain operation failures through event dispatch +10/-7

Retain operation failures through event dispatch

• Constructs and dispatches optional failure events while the operation exception is in flight, preventing observer failures from hiding cancellation and enabling retries.

src/ai/src/Providers/Provider.php

StreamableAgentResponse.phpRetain producer failure through stream catch callbacks +10/-7

Retain producer failure through stream catch callbacks

• Invokes stream failure callbacks without losing the producer's original exception if a callback throws.

src/ai/src/Responses/StreamableAgentResponse.php

PendingConversationTitle.phpMark independent title cancellation as child-owned +5/-1

Mark independent title cancellation as child-owned

• Wraps direct or chained cancellation from concurrent title generation in the existing child-cancellation boundary.

src/ai/src/Support/PendingConversationTitle.php

ClientManager.phpPrefer cancellation during client teardown +5/-6

Prefer cancellation during client teardown

• Keeps cancellation when selecting among disconnect failures, including wrapped failures. No longer suppresses a client exception caused by cancellation.

src/mcp/src/Client/ClientManager.php

Protocol.phpStop handshake fallbacks after cancellation +12/-7

Stop handshake fallbacks after cancellation

• Avoids reconnecting or reporting a legacy handshake failure when an exception chain contains cancellation. Cleanup failure selection now uses the existing cancellation-preference helper.

src/mcp/src/Client/Protocol.php

HttpTransport.phpPreserve cancellation through HTTP transport cleanup +6/-15

Preserve cancellation through HTTP transport cleanup

• Selects cancellation-aware failures when ending exchanges and propagates cancellation from session termination, while retaining resource cleanup.

src/mcp/src/Client/Transport/HttpTransport.php

StdioTransport.phpPreserve cancellation during process disconnect +2/-4

Preserve cancellation during process disconnect

• Uses cancellation-aware failure selection when disconnect itself fails after a transport failure.

src/mcp/src/Client/Transport/StdioTransport.php

Server.phpAvoid JSON-RPC errors for canceled requests +5/-5

Avoid JSON-RPC errors for canceled requests

• Centralizes cancellation detection before converting server exceptions into error responses, covering immediate and streamed handling.

src/mcp/src/Server.php

CompletionComplete.phpDo not turn canceled completion into an empty result +6/-4

Do not turn canceled completion into an empty result

• Propagates completion failures caused by cancellation, including cleanup wrappers, while retaining the empty-result fallback for ordinary exceptions.

src/mcp/src/Server/Methods/CompletionComplete.php

InteractsWithResponses.phpKeep canceled handlers out of tool error responses +9/-5

Keep canceled handlers out of tool error responses

• Propagates chained cancellation from handlers and streamed responses instead of converting it to an MCP error result.

src/mcp/src/Server/Methods/Concerns/InteractsWithResponses.php

CacheInstrumentation.phpPropagate canceled cache-key resolution +5/-3

Propagate canceled cache-key resolution

• Stops cache instrumentation when a key resolver throws direct or wrapped cancellation; ordinary resolver errors remain logged and isolated.

src/opentelemetry/src/Instrumentation/CacheInstrumentation.php

ExceptionInstrumentation.phpStop exception enrichment on cancellation +5/-3

Stop exception enrichment on cancellation

• Propagates cancellation from an enricher instead of logging it and continuing to later callbacks.

src/opentelemetry/src/Instrumentation/ExceptionInstrumentation.php

HttpClientInstrumentation.phpPropagate canceled URL-template resolution +5/-2

Propagate canceled URL-template resolution

• Prevents a canceled URL-template resolver from falling through to the physical HTTP send; ordinary resolver errors remain logged.

src/opentelemetry/src/Instrumentation/HttpClientInstrumentation.php

RedisInstrumentation.phpPropagate canceled Redis query-text resolution +5/-3

Propagate canceled Redis query-text resolution

• Stops Redis instrumentation on resolver cancellation while preserving the fallback for ordinary resolver failures.

src/opentelemetry/src/Instrumentation/RedisInstrumentation.php

UserContextResolver.phpPropagate canceled user-context resolution +5/-3

Propagate canceled user-context resolution

• Recognizes cancellation inside resolver exception chains rather than logging and suppressing it.

src/opentelemetry/src/Support/UserContextResolver.php

EventHandler.phpPropagate cancellation from Sentry event accessors +5/-5

Propagate cancellation from Sentry event accessors

• Lets direct or wrapped cancellation escape the event-handler error boundary while continuing to ignore ordinary instrumentation errors.

src/sentry/src/EventHandler.php

AiIntegration.phpPropagate canceled tool-schema resolution +5/-4

Propagate canceled tool-schema resolution

• Stops AI tracing when user-defined schema hooks are canceled, including through cleanup wrappers; ordinary schema failures remain isolated.

src/sentry/src/Features/AiIntegration.php

CacheIntegration.phpPropagate canceled session-key lookup +5/-1

Propagate canceled session-key lookup

• Prevents session resolution cancellation from being treated as an unavailable session while preserving the existing cleanup and ordinary-error fallback.

src/sentry/src/Features/CacheIntegration.php

Hub.phpPropagate cancellation from Sentry callbacks +13/-8

Propagate cancellation from Sentry callbacks

• Recognizes chained cancellation in breadcrumb, trace-sampling, and profile-sampling callbacks without changing their ordinary-error fallbacks.

src/sentry/src/Hub.php

EventHandler.phpPropagate cancellation from tracing handlers +5/-5

Propagate cancellation from tracing handlers

• Allows cancellation wrapped by instrumentation cleanup to reach application code; unrelated SDK instrumentation errors remain isolated.

src/sentry/src/Tracing/EventHandler.php

JsonNormalizer.phpDo not purge canceled serialization +7/-2

Do not purge canceled serialization

• Propagates cancellation from JSON serialization instead of replacing the value with Telescope's purged marker.

src/telescope/src/JsonNormalizer.php

DatabaseEntriesRepository.phpRetain monitored tags on canceled refresh +6/-1

Retain monitored tags on canceled refresh

• Propagates cancellation from monitored-tag loading without clearing existing tags; ordinary failures still clear them.

src/telescope/src/Storage/DatabaseEntriesRepository.php

Telescope.phpStop recording setup and entries on cancellation +8/-1

Stop recording setup and entries on cancellation

• Propagates cancellation from the pause-recording cache lookup and entry user resolution instead of continuing recording.

src/telescope/src/Telescope.php

ClientRequestWatcher.phpPropagate cancellation while reading HTTP bodies +9/-0

Propagate cancellation while reading HTTP bodies

• Prevents request and response body read cancellation from becoming a purged payload, while retaining stream-position cleanup.

src/telescope/src/Watchers/ClientRequestWatcher.php

DumpWatcher.phpStop dump fallback after cache cancellation +6/-1

Stop dump fallback after cache cancellation

• Propagates cancellation from the dump-watcher cache check; ordinary cache failures still use the existing delegation fallback.

src/telescope/src/Watchers/DumpWatcher.php

Tests (28) +802 / -85
ConcurrentToolsTest.phpCover concurrent child cancellation and sibling joining +23/-4

Cover concurrent child cancellation and sibling joining

• Extends tool-failure coverage to direct and wrapped child cancellation, checking ownership and that sibling tools finish before failure reaches the caller.

tests/Ai/ConcurrentToolsTest.php

AgentEventsTest.phpCover throwing AI failure observers during cancellation +119/-0

Cover throwing AI failure observers during cancellation

• Tests prompt, stream, step, middleware, and tool failure observers that throw while cancellation is in flight.

tests/Ai/Feature/AgentEventsTest.php

UntrustedUrlTest.phpCover wrapped cancellation after oversized transfer +16/-4

Cover wrapped cancellation after oversized transfer

• Checks that direct and cleanup-wrapped transfer cancellation take precedence over the file-size error.

tests/Ai/Feature/Files/UntrustedUrlTest.php

OperationEventsTest.phpEnsure a throwing observer cannot retry cancellation +24/-0

Ensure a throwing observer cannot retry cancellation

• Verifies a failure listener's exception retains the canceled operation as its cause and the operation runs only once.

tests/Ai/Feature/OperationEventsTest.php

RememberConversationTest.phpCover canceled titles and persistence failures +81/-0

Cover canceled titles and persistence failures

• Checks completed-turn fallback after independent title cancellation, plus exception chaining when failed-turn or title-related storage throws.

tests/Ai/Feature/RememberConversationTest.php

StreamableAgentResponseTest.phpCover cancellation through throwing stream callbacks +21/-0

Cover cancellation through throwing stream callbacks

• Verifies a throwing catch callback retains the canceled producer as its previous exception.

tests/Ai/Unit/Responses/StreamableAgentResponseTest.php

ClientManagerTest.phpCover cancellation precedence across client teardown +17/-6

Cover cancellation precedence across client teardown

• Tests direct, wrapped, and client-exception-wrapped cancellation while verifying every owned client is visited.

tests/Mcp/Unit/Client/ClientManagerTest.php

HandshakeTest.phpCover canceled handshake and disconnect paths +55/-7

Cover canceled handshake and disconnect paths

• Checks that chained cancellation stops legacy fallback or reconnect attempts and survives disconnect cleanup.

tests/Mcp/Unit/Client/HandshakeTest.php

HttpTransportTest.phpCover wrapped cancellation during session termination +12/-5

Cover wrapped cancellation during session termination

• Verifies session teardown propagates either form of cancellation while closing the response and forgetting the session.

tests/Mcp/Unit/Client/Transport/HttpTransportTest.php

StdioTransportTest.phpCover cancellation during stdio disconnect and receive +21/-4

Cover cancellation during stdio disconnect and receive

• Exercises direct and wrapped process-stop cancellation, including disconnect triggered after a failed receive, while checking owned-state detachment.

tests/Mcp/Unit/Client/Transport/StdioTransportTest.php

CallToolTest.phpKeep canceled tool handlers out of MCP error results +50/-0

Keep canceled tool handlers out of MCP error results

• Tests wrapped cancellation from both immediate and streamed tool handlers.

tests/Mcp/Unit/Methods/CallToolTest.php

CompletionCompleteTest.phpCover wrapped completion cancellation +12/-2

Cover wrapped completion cancellation

• Extends the empty-completion regression to cancellation wrapped by cleanup.

tests/Mcp/Unit/Methods/CompletionCompleteTest.php

ServerTest.phpCover canceled lazy requests without error responses +18/-5

Cover canceled lazy requests without error responses

• Checks direct and wrapped cancellation preserve request-context restoration and do not send a JSON-RPC error.

tests/Mcp/Unit/ServerTest.php

CacheInstrumentationTest.phpCover canceled cache-key resolvers +12/-5

Cover canceled cache-key resolvers

• Checks direct and wrapped cancellation propagate without cache completion telemetry.

tests/OpenTelemetry/Instrumentation/CacheInstrumentationTest.php

ExceptionInstrumentationTest.phpCover canceled exception enrichers +26/-0

Cover canceled exception enrichers

• Verifies direct and wrapped cancellation stop later enrichers and prevent a completed log record.

tests/OpenTelemetry/Instrumentation/ExceptionInstrumentationTest.php

HttpClientInstrumentationTest.phpCover canceled URL-template resolvers +21/-7

Cover canceled URL-template resolvers

• Checks cancellation prevents a physical send and verifies that a real wrapper failure remains visible in error telemetry.

tests/OpenTelemetry/Instrumentation/HttpClientInstrumentationTest.php

RedisInstrumentationTest.phpCover canceled Redis query resolvers +10/-5

Cover canceled Redis query resolvers

• Checks direct and wrapped cancellation propagate without completion spans or duration metrics.

tests/OpenTelemetry/Instrumentation/RedisInstrumentationTest.php

UserContextResolverTest.phpCover wrapped user-context cancellation +13/-5

Cover wrapped user-context cancellation

• Extends resolver coverage to cleanup-wrapped cancellation and checks the thrown exception remains unchanged.

tests/OpenTelemetry/Support/UserContextResolverTest.php

AuthEventsTest.phpCover canceled authentication user accessors +17/-5

Cover canceled authentication user accessors

• Checks direct and wrapped cancellation from a user accessor reach the caller.

tests/Sentry/EventHandler/AuthEventsTest.php

AiIntegrationTest.phpDistinguish canceled tool schemas from ordinary failures +36/-0

Distinguish canceled tool schemas from ordinary failures

• Verifies wrapped cancellation escapes AI tracing while an ordinary schema exception remains isolated.

tests/Sentry/Features/AiIntegrationTest.php

CacheIntegrationTest.phpCover canceled Redis session resolution +21/-4

Cover canceled Redis session resolution

• Contrasts ordinary session-resolution fallback with wrapped cancellation that stops tracing.

tests/Sentry/Features/CacheIntegrationTest.php

HubTest.phpCover wrapped cancellation in Sentry callbacks +22/-8

Cover wrapped cancellation in Sentry callbacks

• Extends breadcrumb, trace-sampler, and profile-sampler callback cases to cleanup-wrapped cancellation.

tests/Sentry/HubTest.php

EventHandlerTest.phpCover wrapped tracing-handler cancellation +16/-4

Cover wrapped tracing-handler cancellation

• Checks instrumentation cancellation reaches application code both directly and through a cleanup exception.

tests/Sentry/Tracing/EventHandlerTest.php

ExtractPropertiesTest.phpCover cancellation during property serialization +24/-1

Cover cancellation during property serialization

• Verifies a serializer's cleanup-wrapped cancellation escapes property extraction rather than producing a purged value.

tests/Telescope/ExtractPropertiesTest.php

DatabaseEntriesRepositoryTest.phpCover canceled monitored-tag refresh +25/-0

Cover canceled monitored-tag refresh

• Checks wrapped cancellation leaves existing tags intact while ordinary loading failures retain the empty-tags fallback.

tests/Telescope/Storage/DatabaseEntriesRepositoryTest.php

TelescopeTest.phpCover canceled recording and user lookup +45/-0

Cover canceled recording and user lookup

• Verifies cancellation from the recording pause lookup or entry user resolution propagates without enabling recording or queueing the entry.

tests/Telescope/Telescope/TelescopeTest.php

ClientRequestWatcherTest.phpCover canceled request and response body reads +28/-0

Cover canceled request and response body reads

• Uses body-producing streams to verify wrapped cancellation escapes Telescope recording for both payload directions.

tests/Telescope/Watchers/ClientRequestWatcherTest.php

DumpWatcherTest.phpDistinguish dump-cache failures from cancellation +17/-4

Distinguish dump-cache failures from cancellation

• Checks ordinary cache failure still delegates dumps, whereas wrapped cancellation propagates without delegation.

tests/Telescope/Watchers/DumpWatcherTest.php

Documentation (1) +0 / -1
todo.mdRemove completed cancellation-audit task +0/-1

Remove completed cancellation-audit task

• Removes the broad integration-boundary cancellation audit from the task list. The separate proposed static-analysis investigation remains.

docs/todo.md

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@binaryfire

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@binaryfire

Copy link
Copy Markdown
Member Author

The PHPMD parse errors are its lack of support for PHP 8.4 by-reference property hooks. Both files pass the supported PHP runtime and source-analysis CI. No code change is needed. The docstring percentage is not a repository requirement, and the external-contributor checklist does not apply to this maintainer-directed change.

@binaryfire
binaryfire merged commit 9744a7b into 0.4 Nov 2, 2026
46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant