Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpClient abort issues in WASM when streaming #112172

Open
radderz opened this issue Feb 5, 2025 · 13 comments
Open

HttpClient abort issues in WASM when streaming #112172

radderz opened this issue Feb 5, 2025 · 13 comments
Assignees
Labels
arch-wasm WebAssembly architecture area-System.Runtime.InteropServices.JavaScript os-browser Browser variant of arch-wasm
Milestone

Comments

@radderz
Copy link

radderz commented Feb 5, 2025

Description

When doing any form of streaming calls using HttpClient in Blazor WASM, an unhandled exception is shown (red bar by default at the bottom of screen) when there is a connection issue during streaming. I originally posted it in the aspnetcore repo but its a runtime issue.

dotnet/aspnetcore#55982

This can be triggered by a long running IAsyncEnumerable API or GRPC-Web Streaming call, I left a small repo project in the aspnetcore repo bug.

Reproduction Steps

The steps are a reasonably simple but the blazor UI needs to be connected to an api with the ability to remove connectivity (i.e. pulling out the network cable or disconnect wifi or kill the server). So the api needs to be on a different machine or be isolatable from the browser running the blazor wasm UI. Possibly devtunnels would make this easy to reproduce.

  1. Run an API with an IAsyncEnumerable endpoint that runs forever
  2. Run client connected to this endpoint on a different host
  3. Disconnect/break the network connection between the browser and the api to trigger a network error

Expected behavior

The error is bubbled up to the calling C# code that is calling the streaming.

Actual behavior

Raises an unhandled exception that the caller cannot gracefully handle and reconnect.

Regression?

No response

Known Workarounds

None

Configuration

No response

Other information

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 5, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Feb 5, 2025
@radderz
Copy link
Author

radderz commented Feb 5, 2025

It looks to me that

function handle_abort_error (promise:Promise<any>) {
    promise.catch((err) => {
        if (err && err !== "AbortError" && err.name !== "AbortError" ) {
            Module.err("Unexpected error: " + err);
        }
        // otherwise, it's expected
    });
}

Should be changed to

function handle_abort_error (promise:Promise<any>) {
    promise.catch((err) => {
        if (!err) 
               return;        
        if (err === "AbortError" || err.name === "AbortError") 
                return; // normal abort
        if (err === "TypeError: network error" || (err.name === "TypeError" && err.message === "network error")) 
                return; // stream connection failure, should be handled by application

       Module.err("Unexpected error: " + err);
    });
}

Seems like a simple change.

Image

Image

@radderz
Copy link
Author

radderz commented Feb 5, 2025

https://github.com/radderz/BlazorAppJsonStreamBug

You can run the asp.net core project, which is hosting the web assembly page, once you are on the page and the timer is updating, kill the asp.net core api projects process (don't gracefully shut it down, do a kill) and you'll see the unhandled network error.

@maraf
Copy link
Member

maraf commented Feb 5, 2025

cc @pavelsavara

@maraf maraf added arch-wasm WebAssembly architecture area-System.Runtime.InteropServices.JavaScript os-browser Browser variant of arch-wasm and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 5, 2025
Copy link
Contributor

Tagging subscribers to 'arch-wasm': @lewing
See info in area-owners.md if you want to be subscribed.

@pavelsavara pavelsavara self-assigned this Feb 5, 2025
@pavelsavara pavelsavara added this to the 9.0.x milestone Feb 5, 2025
@dotnet-policy-service dotnet-policy-service bot removed the untriaged New issue has not been triaged by the area owner label Feb 5, 2025
@radderz
Copy link
Author

radderz commented Feb 7, 2025

Can this bugfix also be added to dotnet8 as not all of us can use dotnet9

@pavelsavara pavelsavara modified the milestones: 9.0.x, 8.0.x Feb 17, 2025
@pavelsavara
Copy link
Member

@radderz thanks for the investigation.

Matching on the message text is not ideal.
Does the same text of the exception manifest across all the browsers ? (FF, Safari, Edge, Chrome)

@radderz
Copy link
Author

radderz commented Feb 20, 2025

@radderz thanks for the investigation.

Matching on the message text is not ideal. Does the same text of the exception manifest across all the browsers ? (FF, Safari, Edge, Chrome)

@pavelsavara - Not sure I was just guessing how it could be fixed as I don't know how to debug the repo.

@Benuhx
Copy link

Benuhx commented Feb 21, 2025

Can this also be fixed in .NET 9? I'm seeing it with 9.0.200, also with gRPC-web streaming, exactly as described here: dotnet/aspnetcore#55982

@radderz
Copy link
Author

radderz commented Feb 24, 2025

Yea its both 8 and 9, we are moving to 9 soon, i mainly said 8 too please as it'll affect many others that can't move

@pavelsavara
Copy link
Member

pavelsavara commented Feb 27, 2025

Test

chrome streaming req+res

AbortBeforeHeaders - failed in headers - err.name: TypeError err.message: Failed to fetch
AbortAfterHeaders - failed in headers - err.name: TypeError err.message: Failed to fetch
AbortDuringBody - failed in headers - err.name: TypeError err.message: Failed to fetch

chrome full req + streaming +res

AbortBeforeHeaders - failed in headers - err.name: TypeError err.message: Failed to fetch
AbortAfterHeaders - failed in body - err.name: TypeError err.message: network error
AbortDuringBody - failed in body - err.name: TypeError err.message: network error

FF streaming req+res

AbortBeforeHeaders - failed in headers - err.name: TypeError err.message: NetworkError when attempting to fetch resource. 
AbortAfterHeaders - failed in headers - err.name: TypeError err.message: NetworkError when attempting to fetch resource. 
AbortDuringBody - failed in body - err.name: TypeError err.message: Error in input stream

FF full req + streaming +res

AbortBeforeHeaders - failed in headers - err.name: TypeError err.message: NetworkError when attempting to fetch resource. 
AbortAfterHeaders - failed in headers - err.name: TypeError err.message: NetworkError when attempting to fetch resource. 
AbortDuringBody - failed in body - err.name: TypeError err.message: Error in input stream

@pavelsavara
Copy link
Member

After thinking bit more, I realized that the handle_abort_error is there just to suppress the unhandled rejection.
We don't care about the error or message. The rejection will keep flowing to other subscribers of the promise too.

Fix #113014

@radderz
Copy link
Author

radderz commented Mar 2, 2025

Will this fix be done in both dotnet8 and dotnet9?

@pavelsavara
Copy link
Member

Will this fix be done in both dotnet8 and dotnet9?

Yes
#113271
#113261

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arch-wasm WebAssembly architecture area-System.Runtime.InteropServices.JavaScript os-browser Browser variant of arch-wasm
Projects
None yet
Development

No branches or pull requests

4 participants