[grouping info] Improve explanation when exception messages are used
- Dominant language
- Python
- Stars
- 44.8k
- Forks
- 4.9k
- Avg merge
- 21h 10m
- Merged PRs (30d)
- 635
Description
We got a GH issue from a customer who was confused about why we were ignoring their stacktraces and therefore grouping a bunch of different app hang events together. Turns out it was because all frames were marked `ignored` by stacktrace rules, but:
a) Because we prioritize in-app variants, the first stacktrace you saw was a bunch of frames being marked out of app, not ignored. You had to scroll to the system variant to see why they were ignored.
b) The heading for the system variant was `Exception Message (ignored because app exception takes precedence)` when in fact, if we're grouping on message, app and system exception are the same.
c) We do actually say we're ignoring the stacktrace, but it kinda gets lost, visually.
d) You don't actually see any of this unless you click on `All Values`, which isn't that intuitive.
Here is some (redacted, don't worry, Legal) event data and grouping info to illustrate. All but two frames have been removed, but they're enough to show the problem.
Event JSON
We'll need to remove the frame data which gets added by grouping, but I left it in there so we can see it here.
```
{
"platform": "cocoa",
"message": "",
"exception": {
"values": [
{
"type": "Fatal App Hang Fully Blocked",
"value": "The user or the OS watchdog terminated your app while it blocked the main thread for at least 2000 ms.",
"stacktrace": {
"frames": [
{
"function": "__CFSetApplyFunction_block_invoke",
"package": "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation",
"in_app": false,
"data": {
"category": "internals",
"client_in_app": false,
"symbolicator_status": "symbolicated"
}
},
{
"function": "CA::Layer::collect_non_visible_callback",
"raw_function": "CA::Layer::collect_non_visible_callback(void const*, void*)",
"package": "/System/Library/Frameworks/QuartzCore.framework/QuartzCore",
"in_app": false,
"data": {
"category": "internals",
"client_in_app": false,
"symbolicator_status": "symbolicated"
}
}
]
}
}
]
},
"fingerprint": ["{{ default }}"],
"grouping_config": {
"enhancements": "KLUv_WDkAF0HAMIMMC9wa3Mc-k-fsjW-wHC_xVYfq1CbjM1ONPaWmUimfSc4hER17Zek9yTb3_8VtgArA-5V-vDS0ioZQwtX4AL1fUE39yrFrqVgf2kLL88yCEGhMXRzVCLZK2wPfKl3g0iSNAYCqRcLVbUEW3An7FdBZzQ47pxMiWwwQLYmkdQZmuQ5oUjqzISzA08aVCFfSoKKY9yE-FMktQXdHK86HtW96h71Et8BJMf4U1PQLcJ5mgCnEXicZ2A2yG9tPWvcF4M-iA8A3DMADBvbKz6tZeyUtIAWAdOZphbMFD9asDFnBzBcoLqB4RjSqTM#KLUv_WBWAC0FADQIkwORuGFsbC1wbGF0Zm9ybXM6MjAyNi0wMS0yMJuSkatmTG9nZ2VyLmxvZ5LNBAHNAwGSkbFmREJMb2dUYWdnZWRFcnJvcq1BdExldmVsprRmREJBc3NlcnS5ZkRCQ2hlY2vZIE1lc3NhZ2W7tGZGaWxlQ2FjaGWkZmRieJLNBQHNAwEMABkrcSF1BtD7Bs0ctWCj9kECxZkawLg6i4FxhhfzDA#KLUv_WCiAAUHAAJMLS1wS3Wc-8-VaCU_w3Bnkf_nTCspY0MSDUJk0srEXy9kQnlNS2Z2UTPVqo6DaQsLs8kI_ytKtHWTlbiGyjlDZphGMseiOqw1LVJgYIqci2x-28o0obXyRpCmhikA25xUFlPs5RSyjR1whWOfQriRgA4HtxdEmngj_kIYSRNXKnohlorRxfu_gbXv6cJzSpoYQ86F2ZbHNp-3wO97TrGFnBMP5Aw8DgH0QAXw4Z2jtuZ0q4ZkJAIPAD2RdkW3nNAQM2gyQupcQMUbNHPUgo3aBwkUZ2oA4-osBsYZXswz",
"id": "newstyle:2026-01-20"
},
"threads": {
"values": [
{
"id": 0,
"stacktrace": {
"frames": [
{
"function": "__CFSetApplyFunction_block_invoke",
"package": "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation",
"in_app": false,
"data": {
"category": "internals",
"client_in_app": false,
"symbolicator_status": "symbolicated"
}
},
{
"function": "CA::Layer::collect_non_visible_callback",
"raw_function": "CA::Layer::collect_non_visible_callback(void const*, void*)",
"package": "/System/Library/Frameworks/QuartzCore.framework/QuartzCore",
"in_app": false,
"data": {
"category": "internals",
"client_in_app": false,
"symbolicator_status": "symbolicated"
}
}
]
},
"crashed": false,
"current": true,
"main": true
}
]
},
"title": "Fatal App Hang Fully Blocked: The user or the OS watchdog terminated your app while it blocked the main thread for at least 2000 ms.",
"type": "error"
}
```
Grouping Info JSON
```
{
"grouping_config": "newstyle:2026-01-20",
"variants": {
"app_exception_message": {
"type": "component",
"key": "app_exception_message",
"description": "exception message",
"hint": null,
"contributes": true,
"component": {
"id": "app",
"name": "in-app",
"contributes": true,
"hint": null,
"values": [
{
"id": "exception",
"name": "exception",
"contributes": true,
"hint": null,
"values": [
{
"id": "type",
"name": null,
"contributes": true,
"hint": null,
"values": ["Fatal App Hang Fully Blocked"]
},
{
"id": "value",
"name": null,
"contributes": true,
"hint": "stripped event-specific values",
"values": [
"The user or the OS watchdog terminated your app while it blocked the main thread for at least ms."
]
},
{
"id": "stacktrace",
"name": "stacktrace",
"contributes": false,
"hint": "ignored because it contains no in-app frames",
"values": [
{
"id": "frame",
"name": null,
"contributes": false,
"hint": "marked out of app by the client",
"values": [
{
"id": "module",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "filename",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "function",
"name": null,
"contributes": true,
"hint": null,
"values": ["CA::Layer::collect_non_visible_callback"]
}
]
},
{
"id": "frame",
"name": null,
"contributes": false,
"hint": "marked out of app by the client",
"values": [
{
"id": "module",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "filename",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "function",
"name": null,
"contributes": true,
"hint": null,
"values": ["__CFSetApplyFunction_block_invoke"]
}
]
}
]
}
]
},
{
"id": "threads",
"name": "thread",
"contributes": false,
"hint": null,
"values": [
{
"id": "stacktrace",
"name": "stacktrace",
"contributes": false,
"hint": "ignored because it contains no in-app frames",
"values": [
{
"id": "frame",
"name": null,
"contributes": false,
"hint": "marked out of app by the client",
"values": [
{
"id": "module",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "filename",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "function",
"name": null,
"contributes": true,
"hint": null,
"values": ["CA::Layer::collect_non_visible_callback"]
}
]
},
{
"id": "frame",
"name": null,
"contributes": false,
"hint": "marked out of app by the client",
"values": [
{
"id": "module",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "filename",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "function",
"name": null,
"contributes": true,
"hint": null,
"values": ["__CFSetApplyFunction_block_invoke"]
}
]
}
]
}
]
}
]
}
},
"system_exception_message": {
"type": "component",
"key": "system_exception_message",
"description": "exception message",
"hint": "ignored because app exception takes precedence",
"contributes": false,
"component": {
"id": "system",
"name": null,
"contributes": false,
"hint": "ignored because app exception takes precedence",
"values": [
{
"id": "exception",
"name": "exception",
"contributes": false,
"hint": "ignored because hash matches app variant",
"values": [
{
"id": "type",
"name": null,
"contributes": true,
"hint": null,
"values": ["Fatal App Hang Fully Blocked"]
},
{
"id": "value",
"name": null,
"contributes": true,
"hint": "stripped event-specific values",
"values": [
"The user or the OS watchdog terminated your app while it blocked the main thread for at least ms."
]
},
{
"id": "stacktrace",
"name": "stacktrace",
"contributes": false,
"hint": "ignored because it contains no contributing frames",
"values": [
{
"id": "frame",
"name": null,
"contributes": false,
"hint": "ignored by built-in stacktrace rule (category:internals -group)",
"values": [
{
"id": "module",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "filename",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "function",
"name": null,
"contributes": true,
"hint": null,
"values": ["CA::Layer::collect_non_visible_callback"]
}
]
},
{
"id": "frame",
"name": null,
"contributes": false,
"hint": "ignored by built-in stacktrace rule (category:internals -group)",
"values": [
{
"id": "module",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "filename",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "function",
"name": null,
"contributes": true,
"hint": null,
"values": ["__CFSetApplyFunction_block_invoke"]
}
]
}
]
}
]
},
{
"id": "threads",
"name": "thread",
"contributes": false,
"hint": null,
"values": [
{
"id": "stacktrace",
"name": "stacktrace",
"contributes": false,
"hint": "ignored because it contains no contributing frames",
"values": [
{
"id": "frame",
"name": null,
"contributes": false,
"hint": "ignored by built-in stacktrace rule (category:internals -group)",
"values": [
{
"id": "module",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "filename",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "function",
"name": null,
"contributes": true,
"hint": null,
"values": ["CA::Layer::collect_non_visible_callback"]
}
]
},
{
"id": "frame",
"name": null,
"contributes": false,
"hint": "ignored by built-in stacktrace rule (category:internals -group)",
"values": [
{
"id": "module",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "filename",
"name": null,
"contributes": false,
"hint": null,
"values": []
},
{
"id": "function",
"name": null,
"contributes": true,
"hint": null,
"values": ["__CFSetApplyFunction_block_invoke"]
}
]
}
]
}
]
}
]
}
}
}
}
```
Contributor guide
Research direction
The issue names no files, tests, or entry points. Start by tracing the grouping info view and its All Values interaction, then compare how app and system exception-message variants render their hints. Done means the ignored-stacktrace reason is prominent, the system heading accurately reflects matching messages, and the explanation is visible without requiring All Values.
Written by the indexing model from the issue text.
Assessment
- Domain
- frontend
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100