> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt # Issue grouping – Error Tracking pocket guide Canonical URL: https://posthog.com/learn/error-tracking/pocket-guide/issue-grouping > **Cloud region:** This page uses US Cloud hosts. For an EU Cloud project, use `https://eu.posthog.com` instead of `https://app.posthog.com` (app host for links). ## On this page - [How PostHog groups](#how-posthog-groups) - [Default generated fingerprints](#default-generated-fingerprints) - [Custom grouping](#custom-grouping) - [Custom fingerprinting](#custom-fingerprinting) - [Misgrouped issues](#misgrouped-issues) Chapter 03 # Issue grouping – Error Tracking pocket guide PostHog groups exceptions into [issues](https://posthog.com/docs/error-tracking/issues-and-exceptions.md) by their code. Sometimes exceptions that look different in code have the same cause. When a stay's host record is missing, **Trip details** and the stay page both throw a `HostNotFoundError`, from `readTripHost` and `StayPageView`. PostHog puts these exceptions in two issues Fig. 1. IssueVolumeOccurrencesSessionsUsers ![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/js.svg)HostNotFoundError Host "colette" not found for booking booking\_3 readTripHost in src/trips/trip-details.ts1 Active · Unassigned 4 minutes ago 434287251 ![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/js.svg)HostNotFoundError Host "colette" not found for stay stay-03 StayPageView in src/stays/StayPageView.tsx Active · Unassigned 6 minutes ago 1,008612539 Fig. 1 – One missing host record opens two issues in Twig's issue list, one for each function that throws. ## How PostHog groups PostHog decides how to group exceptions into issues by going through these rules, in order: 1. **A custom [fingerprint](https://posthog.com/docs/error-tracking/fingerprints.md) set in source code.** If that's not set: 2. **A [grouping rule](https://posthog.com/docs/error-tracking/grouping-issues.md#option-1-custom-grouping-rule) in PostHog.** If that's not set: 3. **A default fingerprint PostHog generates.** %%{init: {"theme":"base","fontFamily":"RoundHog, sans-serif","themeVariables":{"fontFamily":"RoundHog, sans-serif","primaryColor":"#FFDCA3","primaryBorderColor":"#FFA81C","lineColor":"#1490E8"},"themeCSS":".node rect, .node polygon { fill: rgb(var(--accent)); stroke: rgb(var(--border)); stroke-width: 1.5px; } .node.check rect, .node.check polygon { fill: #FFA81C26; stroke: #FFA81C; } .node.act rect, .node.act polygon { fill: #1490E826; stroke: #1490E8; } .nodeLabel, .edgeLabel { color: rgb(var(--text-primary)); } .flowchart-link { stroke: rgb(var(--text-secondary)) !important; } .marker { fill: rgb(var(--text-secondary)) !important; stroke: rgb(var(--text-secondary)) !important; } .edgeLabel foreignObject { overflow: visible; } span.edgeLabel { background: transparent !important; } .yes, .no { background: rgb(var(--bg)); color: rgb(var(--text-primary)); padding: 0 3px; } .no { position: relative; left: 30px; }"}}%% %%{init: {"block": {"padding": 22}}}%% block-beta columns 3 exception\["New exception"\] space space custom("Custom fingerprint in source code?") space useCustom\["Group by that fingerprint"\] rule("Grouping rule matches?") space useRule\["Group by the rule"\] generated\["Group by a generated fingerprint"\] space space exception --> custom custom -- "yes" --> useCustom custom -- "no" --> rule rule -- "yes" --> useRule rule -- "no" --> generated class custom,rule check class useCustom,useRule,generated act ## Default generated fingerprints PostHog generates the fingerprint from the exception type and Twig's frames in the [stack trace](https://posthog.com/docs/error-tracking/stack-traces.md), or from the message if there's no stack trace. Both exceptions are a `HostNotFoundError`, but one throws in `readTripHost` and the other in `StayPageView` Fig. 2. Different frames make different fingerprints, so PostHog puts them in two issues. HostNotFoundError Host "colette" not found for booking booking\_3 - readTripHostsrc/trips/trip-details.ts@12:111 - openTripDetailssrc/account/AccountPages.tsx@42:18 - renderWithHooksnode\_modules/react-dom/client.js@5654:24 HostNotFoundError Host "colette" not found for stay stay-03 - StayPageViewsrc/stays/StayPageView.tsx@14:112 - renderWithHooksnode\_modules/react-dom/client.js@5654:24 - performUnitOfWorknode\_modules/react-dom/client.js@12561:98 Fig. 2 – The same error from two places. The Twig frames differ, so the fingerprints differ. When a [source map](https://posthog.com/docs/error-tracking/upload-source-maps.md) is absent, PostHog groups by the first in-app frame in the minified code instead. Each deploy can minify that frame differently, so the same bug can land in a new issue after every deploy. ## Custom grouping The engineers want every `HostNotFoundError` in one issue, wherever Twig throws it. They add a grouping rule in [Error Tracking settings](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-custom-grouping) that matches the exception type Fig. 3. New grouping rule Matching exceptions will be grouped as a single issue. FiltersMatchAllAny1 Exception type2 \= equals HostNotFoundError Add filter 1,442 exceptions across 2 issues would have been grouped into one issue in the last 7 days Test ruleCancelSave Fig. 3 – A grouping rule that puts every HostNotFoundError in one issue. Before saving, the editor tests how many exceptions it would have grouped in the last 7 days. The rule only applies to new exceptions, so the engineers also select the two existing issues in the issue list and click **Merge**. ## Custom fingerprinting The grouping rule puts every `HostNotFoundError` in one issue. The engineers want one issue per missing host instead, because each host record needs its own fix. With grouping rules, every host needs its own rule. Twig's code already knows which host is missing, so it's easier to put the host in the fingerprint there Fig. 4. Inspector SDK callcaptureException browser tracking code ``` posthog.captureException(error, { surface: context.surface,1 stay_id: context.stayId,2 $exception_fingerprint: `host-not-found:${error.hostId}`,3}) ``` Fig. 4 – Twig's capture call for a missing host. The fingerprint includes the host, so each missing host gets its own issue. A custom fingerprint wins over the grouping rule. Each missing host now gets its own issue, whether Trip details or the stay page finds it. Like grouping rules, custom fingerprints only apply to new exceptions. ## Misgrouped issues As Twig grows and the team changes, the engineers keep adjusting how they group issues. When issues are misgrouped, Twig's engineers debug like this: %%{init: {"theme":"base","fontFamily":"RoundHog, sans-serif","themeVariables":{"fontFamily":"RoundHog, sans-serif","primaryColor":"#FFDCA3","primaryBorderColor":"#FFA81C","lineColor":"#1490E8"},"themeCSS":".node rect, .node polygon { fill: rgb(var(--accent)); stroke: rgb(var(--border)); stroke-width: 1.5px; } .node.check rect, .node.check polygon { fill: #FFA81C26; stroke: #FFA81C; } .node.act rect, .node.act polygon { fill: #1490E826; stroke: #1490E8; } .nodeLabel, .edgeLabel { color: rgb(var(--text-primary)); } .flowchart-link { stroke: rgb(var(--text-secondary)) !important; } .marker { fill: rgb(var(--text-secondary)) !important; stroke: rgb(var(--text-secondary)) !important; } .edgeLabel foreignObject { overflow: visible; } span.edgeLabel { background: transparent !important; } .yes, .no { background: rgb(var(--bg)); color: rgb(var(--text-primary)); padding: 0 3px; } .no { position: relative; left: 30px; }"}}%% %%{init: {"block": {"padding": 22}}}%% block-beta columns 3 issue\["Misgrouped issue"\] space space frames("Frames unresolved?") space maps\["Fix source maps first"\] right("Grouping looks right?") space leave\["Leave it alone"\] mixed("Mixes different bugs?") space narrow\["Narrow the rule"\] shared("Shared property exists?") space rule\["Add a grouping rule"\] percase("Code decides each case?") space fingerprint\["Set a custom fingerprint"\] add\["Add a context property"\] space space issue --> frames frames -- "yes" --> maps frames -- "no" --> right right -- "yes" --> leave right -- "no" --> mixed mixed -- "yes" --> narrow mixed -- "no" --> shared shared -- "yes" --> rule shared -- "no" --> percase percase -- "yes" --> fingerprint percase -- "no" --> add class frames,right,mixed,shared,percase check class maps,leave,narrow,rule,fingerprint,add act We turned this into a skill for you to debug your misgrouped issues Fig. 5. error-tracking-grouping/SKILL.md ```markdown --- name: error-tracking-grouping description: > Fix a misgrouped PostHog Error Tracking issue. Find the feature the exceptions share, group on an existing property when there is one, and add a shared property through SDK context when there isn't. --- # Fix misgrouped issues Read the current docs first. Every PostHog docs URL returns Markdown if you append `.md`: - https://posthog.com/docs/error-tracking/grouping-issues.md - https://posthog.com/docs/error-tracking/fingerprints.md - https://posthog.com/docs/libraries/python.md (contexts) - https://posthog.com/docs/libraries/node.md (contexts) - https://posthog.com/docs/libraries/js/usage.md (super properties) Walk each issue through these checks in order, and stop at the first yes. ## 1. Are the frames unresolved? If the frames point into minified bundles, fix source maps first. Unresolved frames change with every deploy, so one bug keeps opening new issues. ## 2. Does the grouping look right? One issue should be one bug that one change fixes. Compare the exception type, message, and top in-app frames of each issue. If each issue is one bug, leave it alone. ## 3. Does one issue mix different bugs? A grouping rule or fingerprint is too broad. Narrow it rather than adding another rule. ## 4. Does a shared property exist? Look for a feature every exception in the group has and no exception outside it has. Check the exception event first: `$exception_types`, `$exception_values`, `$exception_sources`, the URL, and any custom properties. Then check the code: the service, route, job, or feature where they're thrown. If an existing property identifies the group, add a grouping rule in Error Tracking settings. Filter on that property, test the match count, and place the rule before broader rules. The first matching rule wins. No deploy needed. Rules only apply to new exceptions, so merge the issues that already exist from the issue list. ## 5. Does the code decide each case? If the code must pick the group for each exception, set `$exception_fingerprint` at capture. For autocaptured exceptions, set it in `before_send`. Also set `$issue_name` and `$issue_description`. ## 6. Otherwise, add a context property Add one property and set it once, at the layer every exception passes through. Don't add it at each throw site. | Where the exceptions come from | Set the property with | | --- | --- | | A Python request, job, or task | `with new_context(): tag("key", value)` at the entry point | | A Node request or job | `posthog.withContext({ properties: {...} }, fn)` in middleware | | A browser app area | `posthog.register({...})` when the area loads | | One known call site | Properties on `captureException(error, {...})` | Then add a grouping rule on the new property, as in step 4. ## Rules 1. Don't group to route ownership or to filter. Use assignment rules for that. A property you add in step 6 works for both. 2. Never put a catch-all grouping rule first. ## Finally List each issue you changed, the property you grouped on, and whether you added it. Say which changes need a code deploy. ``` Show full example Fig. 5 – The skill Twig's engineers use to debug misgrouped issues. Copy it and give it to your agent. [Reduce noise →](https://posthog.com/learn/error-tracking/pocket-guide/reduce-noise.md) **Explore further** [Grouping exceptions into issues](https://posthog.com/docs/error-tracking/grouping-issues.md) · [Fingerprints](https://posthog.com/docs/error-tracking/fingerprints.md)