aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

`HubException` message is always "wrapped", contrary to documentation

Open milesizzo opened this issue 2 weeks ago • 2 comments

Is there an existing issue for this?

  • [x] I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

According to the SignalR documentation, when throwing a HubException from a hub method, SignalR should "[send] the entire exception message to the client, unmodified." However, the actual behavior wraps the message:

// Server
throw new HubException("{\"code\":0}");

// Client receives
"An unexpected error occurred invoking 'MethodName' on the server. HubException: {\"code\":0}"

This is caused by ErrorMessageHelper.BuildErrorMessage() which - in the case of HubException - formats the message as: https://github.com/dotnet/aspnetcore/blob/a9aaa320f1c4c771b2dee8c000409a5f04397339/src/SignalR/server/Core/src/Internal/ErrorMessageHelper.cs#L10-L13

This occurs when throwing the exception from a hub method and from a filter.

Describe the solution you'd like

The HubException.Message should be passed through unmodified to the client, as documented.

Alternatively, update the documentation to clarify this behaviour.

Additional context

Here is a sample use-case:

Passing JSON error payloads to clients:

// Server
throw new HubException("{\"code\":\"USER_NOT_FOUND\"}");

// Client receives
"An unexpected error occurred invoking 'GetUser' on the server. HubException: {\"code\":\"USER_NOT_FOUND\"}"

The client cannot directly deserialize this as JSON without first stripping the prefix.

This is similar to the following issues:

  • #7722
  • #12633
  • #11891

However I feel it is different enough to warrant a separate report.

If this behaviour is not desired, what is the recommended method for handling/parsing errors on the client?

milesizzo avatar Dec 09 '25 13:12 milesizzo

Sure, we could be a little clearer in the docs. A few lines up in the doc we give an example of a non-HubException error that's returned to the client, so we can probably include an example with a HubException.

If you want to parse the error then it should be easy to strip the prefix JSON.parse(error.split('HubException: ')[1])

BrennanConroy avatar Dec 09 '25 20:12 BrennanConroy

Parsing out the string from SignalR feels like a fragile solution for dealing with errors. Is there a reason that explicit HubException exceptions thrown by user code need to be wrapped?

Having the ability to create an IHubFilter that catches known exceptions in hub methods and return well-formed error messages/codes is quite powerful (and follows a similar pattern to ASP .NET Core IExceptionFilter). If the appetite for changing the existing implementation is not there, introducing an IHubExceptionFilter interface to customize the error handling could also be a nice addition. This could be made backwards compatible (e.g. no exception filter = current behaviour).

milesizzo avatar Dec 09 '25 23:12 milesizzo