You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Improve handling of HttpResponseError objects being captured in Angular SDK #15239
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.
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.
@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.
Thank you @klyakh for your comment! Armed with that knowledge, we asked Claude for a little proof of concept.
import{HttpErrorResponse}from'@angular/common/http';/** * Custom error class for HTTP errors that provides better Sentry grouping. * Errors are grouped by URL path (without query params) and status code. * * Includes request metadata (method, body) if available from the interceptor. */exportclassHttpErrorextendsError{readonlystatus: number;readonlystatusText: string;readonlyurl: string;constructor(httpErrorResponse: HttpErrorResponse){// Extract the path from the URL (remove query params for better grouping)consturlPath=extractUrlPath(httpErrorResponse.url||'unknown');// Create a descriptive message that includes status and URLconstmessage=`HTTP ${httpErrorResponse.status}${httpErrorResponse.statusText||'Error'}: ${urlPath}`;super(message);this.name='HttpError';this.status=httpErrorResponse.status;this.statusText=httpErrorResponse.statusText;this.url=httpErrorResponse.url||'unknown';this.responseBody=httpErrorResponse.error;if(httpErrorResponse['stack']){this.stack=httpErrorResponse['stack'];}}}/** * Extracts the path portion from a URL, removing query parameters and fragments. * This helps with grouping similar errors together in Sentry. * * @example * extractUrlPath('https://api.example.com/users/123?foo=bar') // returns '/users/123' * extractUrlPath('https://api.example.com/trips/456/book') // returns '/trips/456/book' */exportfunctionextractUrlPath(url: string): string{try{constparsedUrl=newURL(url);returnparsedUrl.pathname;}catch{// If URL parsing fails, try to extract path manuallyconstpathMatch=url.match(/https?:\/\/[^/]+(\/[^?#]*)/);returnpathMatch?.[1]||url;}}/** * Checks if an error is an Angular HttpErrorResponse. */functionisHttpErrorResponse(error: unknown): error is HttpErrorResponse{return(error!==null&&typeoferror==='object'&&(errorasHttpErrorResponse).name==='HttpErrorResponse');}/** * Custom error extractor for Sentry that properly handles Angular HttpErrorResponse. * * When an HttpErrorResponse is captured, this extractor converts it to a proper * Error object that Sentry can understand and group correctly. * * Errors are grouped by: * - URL path (without query parameters) * - HTTP status code * * @example * Sentry.createErrorHandler({ * extractor: httpErrorExtractor, * }); */exportfunctionhttpErrorExtractor(error: unknown,defaultExtractor: (error: unknown)=>unknown): unknown{// First, use the default extractor to unwrap any Angular error wrappingconstextractedError=defaultExtractor(error);// Check if the extracted error is an HttpErrorResponseif(isHttpErrorResponse(extractedError)){returnnewHttpError(extractedError);}// Also check the original error in case it wasn't properly extractedif(isHttpErrorResponse(error)){returnnewHttpError(error);}// For all other errors, return the default extracted errorreturnextractedError;}
This works a lot better than the out of box experience, but grouping of events etc. is still not quite there. But it's a starting point.
We're also experimenting with an HTTP Interceptor to annotate these errors with the request body that caused it.
Activity
Lms24 commentedon Jan 31, 2025
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!):
ingestthe issues team about this15 remaining items
klyakh commentedon Nov 29, 2025
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
HttpErrorResponseinstances 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:
HttpErrorResponse, something like:Http failure response for https://mydomain.com/api/asset/search?page=1&pageSize=50&keyword=test: 401 UnauthorizedHttp failure response for <url> <int> UnauthorizedSo 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
HttpErrorResponseFrom what I see in
extractHttpModuleErrorin packages/angular/src/errorhandler.ts,HttpErrorResponse.erroris only used if it’s in one of these shapes:instanceof Error{ name: string; message: string; stack?: string }instanceof ErrorEventstringIf 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:<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
HttpErrorResponseinto a customErrorsubclass, e.g.HttpErrorResponseError. That class contains:HttpErrorResponse.errorSo instead of sending just the simple string, I send this custom error object to Sentry. I then:
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.
logaretm commentedon Dec 1, 2025
@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.
Lms24 commentedon Dec 1, 2025
Thank you for the great response! Just a couple of thoughts from my end:
Could you link to such an event in Sentry? This makes it a bit easier for us to understand which grouping rules were applied.
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.
MarcoGlauser commentedon Jan 21, 2026
Thank you @klyakh for your comment! Armed with that knowledge, we asked Claude for a little proof of concept.
This works a lot better than the out of box experience, but grouping of events etc. is still not quite there. But it's a starting point.
We're also experimenting with an HTTP Interceptor to annotate these errors with the request body that caused it.
chargome commentedon Jan 22, 2026
Could anyone actually running into this share a link to an event in Sentry?