Improve handling of `HttpResponseError` objects being captured in Angular SDK
Is there an existing issue for this?
- [x] I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- [x] I have reviewed the documentation https://docs.sentry.io/
- [x] I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/angular
SDK Version
8.52.0
Framework Version
19.1.0
Link to Sentry event
No response
Reproduction Example/SDK Setup
https://github.com/cobyeastwood183/angular
{
"headers": {
"headers": {},
"normalizedNames": {},
"lazyUpdate": null
},
"status": 0,
"statusText": "Unknown Error",
"url": "http://some.example.com/test",
"ok": false,
"name": "HttpErrorResponse",
"message": "Http failure response for http://some.example.com/test: 0 Unknown Error",
"error": {
"isTrusted": true
}
}
Steps to Reproduce
- npm install
- add DSN
- npm start
- visit http://localhost:4200/test
- click button "Test API 2(Localhost)"
Expected Result
Expect a HttpException to be captured for HttpErrorResponse.
Actual Result
Instead 'unknown' is captured when caught by the ErrorHandler or 'Object.error' when caught by calling captureException.
Dumping my thoughts from the slack conversation here. I don't have time to look at this today but will continue on Monday.
I’ll summarize my preliminary findings from checking out the repro (thanks a lot btw!):
- If you call captureException when handling the error, it won’t go through the ErrorHandler but will be sent directly to Sentry. In this case, the error object ends up with the Exception Captured With Keys message that you encountered.
- Maybe we can apply the same extraction logic as in the ErrorHandler within the Angular SDK. let me think about this for a bit.
- Letting the error bubble up via the ErrorHandler, leads to the
error in Sentry. - The “
” error type must have been changed recently in the product or during ingest because we add a title property to the event which is “ ” . The SDK doesn’t set title at all. So some kind of inference logic is going wrong here… I asked ~ingest~ the issues team about this - The other problem is that we capture much less information about this error than if we’d directly call captureException on it directly. Most importantly, we loose the stack trace
- We could add a stack trace but due to Angular’s ZoneJS usage, it’s pretty worthless because it doesn’t point back to the user source code. Not sure yet if we can solve this but it doesn’t look trivial.
- The “
I improved HttpErrorResponse handling in my Angular project by implementing a custom error extractor.
The extractor creates a custom Error object that includes debug info from the original HttpErrorResponse.
I also configured event fingerprinting for this custom error type to group errors with better granularity.
Now issues in Sentry look like this:
HttpErrorResponseError /Services/Asset/Deactivate: 400 – Missing 'date'
- First line - custom error type
- Second line - custom error message with details from
HttpErrorResponse. It includes request path, response code and server message.
Hey @klyakh thanks for letting us know! Are you interested in contributing your extraction logic as a PR? Perhaps we can combine it with our existing extractor to improve the errors for everyone. No pressure, just let us know :)
This issue has gone three weeks without activity. In another week, I will close it.
But! If you comment or otherwise update it, I will reset the clock, and if you remove the label Waiting for: Community, I will leave it alone ... forever!
"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀
Hi @Lms24 , I’m not ready to open a PR yet, because the code I have on my side feels more like a workaround tailored to my app than a clean, generic solution. But I can share what I found and how I’m currently handling it – maybe that helps.
1. Main problem: different endpoints end up in the same Issue
Right now, different HttpErrorResponse instances from different API endpoints are grouped into a single Issue. That makes it hard to use Sentry in a normal workflow: one Issue may contain many different root causes, some already fixed, some not.
In my setup, this is what happens:
- The Angular integration extracts only a string (not an Error object) from
HttpErrorResponse, something like:Http failure response for https://mydomain.com/api/asset/search?page=1&pageSize=50&keyword=test: 401 Unauthorized - Grouping then happens by the stripped exception value. After stripping, this becomes something like:
Http failure response for <url> <int> Unauthorized - Because the URL is normalized away, errors from different endpoints are grouped together.
So I lose the distinction between different API endpoints, which is often exactly what I want to group by.
2. Why only a simple string is captured from HttpErrorResponse
From what I see in extractHttpModuleError in packages/angular/src/errorhandler.ts, HttpErrorResponse.error is only used if it’s in one of these shapes:
-
instanceof Error -
{ name: string; message: string; stack?: string } -
instanceof ErrorEvent -
string
If it’s none of those, it’s effectively ignored and the code falls back to HttpErrorResponse.message. That’s how we end up with a plain string like the example above.
In my case, the server returns a structured JSON for server-side errors, e.g.:
{
"message": "An error has occurred.",
"exceptionMessage": "This is test server error",
"exceptionType": "System.NotImplementedException",
"stackTrace": "<server-side stack trace here>"
}
This entire JSON ends up in HttpErrorResponse.error, but because it doesn’t match any of the four patterns, it’s ignored. As a result:
- The meaningful fields from the server response are not visible in the Sentry event.
- I also see
<unknown>as the Issue title in the UI for these cases.
3. My current workaround
To work around this, I wrote a custom extractor that turns an HttpErrorResponse into a custom Error subclass, e.g. HttpErrorResponseError. That class contains:
- endpoint URL
- status code
- the raw payload from
HttpErrorResponse.error
So instead of sending just the simple string, I send this custom error object to Sentry. I then:
- add the structured server payload as part of the error (message/extra), and
- tune the fingerprint logic to group events based on properties of this custom error (for example, using the HTTP method + path + status).
This gives me usable grouping and much richer event data, but it feels quite hacky and app-specific.
Happy to share more details if that helps.
@klyakh Thanks a lot of the detailed explanation, It would be great if you can share more details about the fingerprinting and enriched payload.
I think it may not be as app-specific as you think. Grouping by URL seems reasonable to me as a default but if you can share details, I can discuss them with the team and see if you (or we) could PR this.
Thank you for the great response! Just a couple of thoughts from my end:
Grouping then happens by the stripped exception value. After stripping, this becomes something like: Http failure response for
Unauthorized
Could you link to such an event in Sentry? This makes it a bit easier for us to understand which grouping rules were applied.
Grouping by URL seems reasonable to me as a default
Agreed, as long as we don't have a client-side stack trace. I need to do some more investigative work to see if that's the case. My thinking is that there's likely a different stack trace/call stack for different endpoints, so ideally the errors shouldn't be grouped together in the first place. If we don't have a stack trace, falling back to the URL (and status code?) sounds reasonable to me on first glance. We could adjust the default fingerprint for HttpResponseErrors from within the SDK.