Repository navigation
fix(core): surface activity processing timeouts - #690
Conversation
When ProcessActivityTimeout elapses, BotApplication.ProcessAsync now throws BotHandlerException wrapping a TimeoutException (with the activity attached) instead of returning normally. HTTP returns 500, Socket Mode replies/acks 500, and the BotBuilder adapter invokes OnTurnError, rather than reporting success. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
Review effort: Lite
Findings: 4
Open (4)
Using a 50ms processing timeout here may make theOnTurnErrortimeout assertion flaky on slower… · New Using a 50ms processing timeout here may make theOnTurnErrortimeout assertion flaky on slower… · New This test also uses a 50ms timeout, which is prone to intermittent failures under CI contention.… · New The 50ms timeout is very tight for CI/load variability and can make these tests flaky (timer… · New
What changed in this PR
Makes ProcessActivityTimeout surface as a visible failure by throwing a BotHandlerException (with TimeoutException inner) instead of logging and returning successfully, ensuring transports report the turn as failed.
Changes:
- Throw
BotHandlerException("Activity processing timed out", inner: TimeoutException, activity)when processing exceedsProcessActivityTimeout. - Update XML docs to describe timeout behavior on both
ProcessAsyncoverloads andBotApplicationOptions.ProcessActivityTimeout. - Add unit tests validating timeout behavior across Core, Socket Mode hosting, and BotBuilder adapter (
OnTurnError).
| File | Description |
|---|---|
| test/Microsoft.Teams.Core.UnitTests/BotApplicationTests.cs | Adds/updates tests asserting timeouts now throw BotHandlerException with TimeoutException inner. |
| test/Microsoft.Teams.Apps.UnitTests/SocketMode/SocketModeHostingTests.cs | Adds Socket Mode dispatch test asserting timeout throws BotHandlerException. |
| test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatAdapterTests.cs | Adds adapter test asserting timeout flows to OnTurnError as BotHandlerException with TimeoutException inner. |
| src/Microsoft.Teams.Core/Hosting/BotApplicationOptions.cs | Documents that timeout elapsing results in a failing turn via thrown BotHandlerException. |
| src/Microsoft.Teams.Core/BotApplication.cs | Updates docs and implements throwing behavior on activity processing timeout. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Corina (corinagum)
left a comment
There was a problem hiding this comment.
Could the description mention that long-running streaming turn after 5 mins now gets a 500?
Also, design docs on decoupling cancellation tokens are now stale. They need to be updated or removed.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Corina (corinagum)
left a comment
There was a problem hiding this comment.
LGTM, thanks!
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

When
ProcessActivityTimeoutelapsed,BotApplication.ProcessAsynclogged the timeout and then returned normally, so the turn was reported as a success. HTTP returned 200, and Socket Mode replied or acked 200 (flagged in review on #686). This change makes timeouts fail visibly instead.Change
The timeout catch in
ProcessAsync(CoreActivity, ...)still writes the log, theHandlerErrorsmetric and the error span status. It then throwsBotHandlerException("Activity processing timed out", new TimeoutException(...), activity).No transport code changed. Each transport already handles
BotHandlerException:{ error = "bot handler error" }for invokes, 500 ack otherwise), plus the error hookTeamsBotFrameworkHttpAdapterOnTurnErroris calledTimeoutExceptionthrown by a handlerBotHandlerException("Error processing activity")The XML docs on both
ProcessAsyncoverloads and onBotApplicationOptions.ProcessActivityTimeoutnow describe the new behavior.Notes for reviewers
docs/design-decouple-cancellation-token.mddocumented that decision; its timeout-handling section is updated in this PR to describe the new behavior.ProcessActivityTimeout(default 5 minutes) and observes its cancellation token, such as a long LLM stream, now fails instead of completing silently. It logsActivityTimedOutat Error and returns a 500. On Socket Mode the 500 is sent as the envelope reply. On HTTP, Teams has usually closed the connection long before 5 minutes, so the 500 mainly shows up in server logs. Bots that legitimately need longer turns can raiseProcessActivityTimeoutor set it toTimeout.InfiniteTimeSpan.TimeoutException, and aTimeoutExceptionthrown by a handler is wrapped the same way, soInnerException is TimeoutExceptiondoes not identify a framework timeout. Transports don't need the distinction (both cases fail the turn), and logs already separate them (ActivityTimedOut, EventId 5, vsActivityProcessingError, EventId 6, plus span status"timeout"). A public type such asProcessActivityTimeoutExceptioncan be added later if a consumer needs to distinguish them in code.Tests
BotHandlerExceptionwith an innerTimeoutExceptionand the activity attached. A handler that throws its ownTimeoutExceptionstill gets the existing wrapping.SocketModeServiceRegistration.DispatchAsyncthrows on timeout. The conversion to a 500 reply is already covered bySocketModeTransportTests.OnTurnErrorreceives the timeout exception.dotnet build Microsoft.Teams.slnxsucceeds. Core (205) and Apps (829) unit tests pass on net8.0 and net10.0, and BotBuilder (57) passes on net10.0, the only framework it targets.