Error Fingerprinting
also called Issue Grouping, Event Deduplication
Deriving a stable identifier from an error's invariant attributes so that many occurrences collapse into one actionable issue - and the two directions in which the heuristic fails damagingly.
Raw error events are nearly useless. Ten thousand occurrences of one bug is one thing to fix, and a platform presenting ten thousand events has transferred the aggregation work to the user at the worst possible moment.
Fingerprinting derives a stable key from the invariant parts of an error — exception type, the top frames belonging to application code, the code location — so that occurrences collapse into an issue.
Why it matters
Aggregation is the product. It is also a heuristic, and both failure directions are damaging: over-grouping merges distinct bugs and hides one behind another; under-grouping creates thousands of issues for one bug and drowns the signal it was meant to surface.
Implementation patterns
- Normalise before fingerprinting: strip numbers from messages, collapse anonymous function names, remove file paths that vary by deployment, exclude memory addresses and identifiers. Any variable data included makes every occurrence unique.
- Prefer application frames over library frames, since two different bugs frequently share a library frame at the top of the stack.
- Allow the user to override the fingerprint. The heuristic will be wrong for their code, and a platform that cannot be corrected is wrong permanently.
- Version the grouping algorithm and apply changes only to new events. Regrouping historical issues splits or merges what a user has already triaged, destroying their work.
- Sample at the client for high-frequency errors, sending a proportion with a recorded multiplier — the thousandth identical event adds nothing.
- Rate limit per project and communicate it clearly, because silent dropping during an incident is exactly when the data is needed.
- Keep full detail for a sample and accurate aggregate counts for the rest, which is more useful than partial detail on everything.
Industry example
Error-tracking platforms such as Sentry are built entirely around this trade. The retention design follows from it: errors have a sharp relevance curve — today's error is being worked on, last month's is fixed or accepted — so full event detail is retained briefly while aggregate counts and trends are retained long. That matches usage and is far cheaper than uniform retention.
High-cardinality context — user, release, device, feature flag state — is what makes an error actionable and what makes storage expensive, and the resolution is that it lives on sampled events rather than in an index over all events.
Failure scenarios
- Variable data in the fingerprint, making every occurrence a new issue.
- Fingerprinting on a library frame, merging unrelated bugs.
- Regrouping historical events, destroying triage work.
- No user override, leaving the heuristic's errors permanent.
- Silent rate limiting during an incident, hiding data at the moment of maximum need.
- Uniform retention, paying archival cost for detail that is worthless after a week.
Trade-offs
Aggressive normalisation risks over-grouping; conservative normalisation risks under-grouping. There is no setting correct for all codebases, which is precisely why the override matters more than the default.
Sampling loses individual occurrences, which is acceptable for a bug seen a million times and unacceptable for one seen twice. Sampling rates should therefore be a function of observed frequency rather than a fixed value, so rare errors are always captured in full and common ones are not.
Interview question
"Two genuinely different bugs are being grouped as one issue, and one bug is producing four hundred separate issues. Both are in the same customer's project. What is going wrong in each case and what do you change?"