Skip to content

Avoid starting the response when navigating with exception - #69174

Open
ilonatommy wants to merge 4 commits into
dotnet:mainfrom
ilonatommy:do-not-start-response-on-navigating
Open

ilonatommy wants to merge 4 commits into
dotnet:mainfrom
ilonatommy:do-not-start-response-on-navigating

Conversation

@ilonatommy

@ilonatommy ilonatommy commented Sep 9, 2026 •

Copy link
Copy Markdown
Member

Avoid starting the response when task didn't finish successfully due to the by-design navigation exception.

Description

Cookie-backed [SupplyParameterFromTempData] values were lost when a non-streaming static SSR EditForm handler redirected using exception-driven navigation.

The redirect succeeded, but the response was accidentally routed through the streaming response path before TempData persistence. This started the response and prevented the TempData provider from adding its cookie.

Background

During static SSR, NavigationManager.NavigateTo supports two control-flow modes:

  • Old exception-driven navigation: NavigateTo throws NavigationException, which the renderer catches and converts into a redirect.
  • New, non-throwing navigation: NavigateTo directly invokes the endpoint navigation callback.

Exception-driven navigation remains the runtime compatibility default when BlazorDisableThrowNavigationException is absent or false. This means newly created applications normally use non-throwing navigation, while existing applications upgraded to .NET 11 can continue using exception-driven navigation.

When NavigateTo throws inside HandleSubmitAsync, the exception becomes the failure of the task returned by HandleSubmitAsync. The renderer tracks that task as non-streaming pending work.

Root cause

WaitForNonStreamingPendingTasks correctly caught the task's NavigationException and established the redirect before the response started. However, the separate full-quiescence task retained by RazorComponentEndpointInvoker remained non-successful:

IsCompleted             = true
IsFaulted               = true
IsCompletedSuccessfully = false

The invoker used !quiesceTask.IsCompletedSuccessfully to decide whether to call SendStreamingUpdatesAsync. As a result, the already-handled navigation fault was mistaken for streaming work.

SendStreamingUpdatesAsync flushed the response before awaiting the faulted task and then emitted a streaming redirection template. By the time TempDataService.Persist ran, Response.HasStarted was true, so the cookie provider could no longer add the TempData cookie.

This was what was meant by

"false streaming" mode in some cases,

in #68796 (comment).

Fix

WaitForNonStreamingPendingTasks reports whether it handled a NavigationException to avoid starting streaming in navigation-by-exception flows.

Before reporting navigation as handled, it inspects the original Task.WhenAll exception collection. If the batch contains any non-navigation exception, that exception is propagated instead of being treated as successful navigation.

The invoker excludes the quiescence task from the streaming path only when:

  1. The non-streaming waiter handled navigation.
  2. The full quiescence task is faulted.
  3. All exceptions in that task are NavigationException.

Fixes #68796

… successfully due to the by-design naviagaion exception.
Copilot AI lite review requested due to automatic review settings September 9, 2026 14:25
@ilonatommy
ilonatommy requested a review from a team as a code owner September 9, 2026 14:25
@github-actions github-actions Bot added the area-blazor Includes: Blazor, Razor Components label Sep 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The change is narrowly scoped to navigation-exception handling, updates call sites consistently, and adds both unit and E2E coverage for the reported regression scenario.

Pull request overview

This PR fixes a static SSR TempData persistence regression when legacy exception-driven NavigationManager.NavigateTo is used from a non-streaming form handler (notably EditForm). It ensures handled NavigationException faults don’t incorrectly route the response through the streaming update path, which could start/flush the response before cookie-backed TempData is persisted.

Changes:

  • Update EndpointHtmlRenderer.WaitForNonStreamingPendingTasks to return whether it handled a NavigationException, while still propagating mixed (non-navigation) failures.
  • Prevent RazorComponentEndpointInvoker from entering streaming updates when the only reason quiescence is faulted is a handled navigation exception.
  • Add coverage: an E2E repro via EditForm + [SupplyParameterFromTempData], and unit tests for the new pending-task navigation handling.
File summaries
File Description
src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/TempData/TempDataComponent.razor Adds an EditForm POST path to exercise legacy navigation-by-exception with TempData.
src/Components/test/E2ETest/Tests/TempDataCookieTest.cs Adds E2E coverage ensuring cookie TempData persists across an EditForm-driven redirect in the legacy navigation flow.
src/Components/Endpoints/test/EndpointHtmlRendererTest.cs Adds unit tests validating WaitForNonStreamingPendingTasks reports handled navigation and doesn’t mask mixed exceptions.
src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.Prerendering.cs Changes WaitForNonStreamingPendingTasks to return a bool and inspects aggregated exceptions to avoid treating mixed failures as successful navigation.
src/Components/Endpoints/src/Rendering/EndpointHtmlRenderer.cs Updates the cached completion task type for non-streaming pending tasks to Task<bool>.
src/Components/Endpoints/src/RazorComponentEndpointInvoker.cs Skips streaming updates when navigation was already handled and the remaining quiescence fault contains only NavigationExceptions.
Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Looks like this PR hasn't been active for some time and the codebase could have been changed in the meantime.
To make sure no conflicting changes have occurred, please rerun validation before merging. You can do this by leaving an /azp run comment here (requires commit rights), or by simply closing and reopening.

@dotnet-policy-service dotnet-policy-service Bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Sep 29, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cookie TempData fails on non-streaming SSR EditForm redirect with legacy navigation behavior

4 participants