grouping-noisy-errors
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGrouping noisy errors
对噪声错误进行分组
The same error can be reported as dozens of separate issues when stack frames or
messages contain volatile data — random IDs, dynamic file paths, build hashes,
anonymous function names. The fix is two-step: merge the existing issues into one
target, then create a grouping rule so future events from the same call site
share a single canonical fingerprint instead of spawning new ones.
Important up front: "same error" here is narrow. Two issues that share a name or
a sentence of message text but came from different code paths, different SDKs,
or different runtimes are different errors and should stay separate, even if
the user thinks of them as "the same kind of bug". Grouping a frontend
together with a backend because both messages contain
"undefined" destroys the signal that lets the team find each one. The criteria
in step 1 exist to keep that from happening.
TypeErrorTypeError同一错误可能因堆栈帧或消息包含易变数据(随机 ID、动态文件路径、构建哈希、匿名函数名)而被报告为几十个独立问题。解决方案分为两步:先将现有问题合并到一个目标,再创建分组规则,使未来来自同一调用位置的事件共享一个标准指纹,而不是不断生成新指纹。
首先需要明确:“同一错误”的含义非常狭窄。两个问题即使名称相同或消息文本中包含相同的句子,但如果来自不同的代码路径、不同的 SDK 或不同的运行时,它们仍然是不同错误,应当保持分离,即使用户认为它们是“同一类 bug”。如果仅仅因为前端和后端的 消息都包含“undefined”,就将它们分组在一起,就会破坏团队定位每个问题的信号。步骤 1 中的判定标准正是为了防止这种情况。
TypeErrorAvailable tools
可用工具
| Tool | Purpose |
|---|---|
| Find candidate duplicate issues |
| Pull compact details for an individual issue |
| Sampled |
| Merge existing issues into a target |
| Surgically split fingerprints back out if a merge errs |
| Auto-group future events into one issue |
| Check existing grouping rules before adding new ones |
| Rename or re-describe the target after a merge |
| 工具 | 用途 |
|---|---|
| 查找候选重复问题 |
| 获取单个问题的简要详情 |
| 获取采样的 |
| 将现有问题合并到目标问题 |
| 合并有误时,精确地将指纹重新拆分出来 |
| 将未来事件自动归组到一个问题 |
| 添加新规则前检查现有分组规则 |
| 合并后重命名或重新描述目标问题 |
Merge vs grouping rule
合并与分组规则
The two tools solve different halves of the problem:
- Merge is one-shot. It collapses existing issues into a target and re-attaches their events. Future events still group by their original fingerprints — if the same noisy pattern keeps producing new fingerprints, merging is a treadmill.
- Grouping rule is durable. It rewrites the fingerprint of any matching
event to at ingestion time, so all future matches share one canonical fingerprint rather than spawning new ones. The first match either creates a new issue keyed off that fingerprint, or routes to whatever issue is already bound to it.
custom-rule:<rule_id>
Use both together when the issue is recurring: merge historical duplicates
into a target issue, then create the rule. The rule API does not accept a
target issue ID — once the rule starts firing, the resulting
issue can be merged into the same target so the consolidation sticks. Use
merge alone for historical sprawl that you don't expect to recur. Use a
grouping rule alone for a brand-new pattern you're getting ahead of, when
you don't need to consolidate with an existing issue.
custom-rule:...这两种方式分别解决问题的两个不同部分:
- 合并是一次性操作。它会将现有问题归并到目标问题,并重新关联这些问题的未来事件。未来事件仍会按照原始指纹分组——如果同一种噪声模式持续产生新指纹,合并就只是在徒劳地重复劳动。
- 分组规则是持久化机制。它会在摄取时将任何匹配事件的指纹重写为 ,使所有未来匹配项共享一个标准指纹,而不是不断生成新指纹。首次匹配时,要么创建一个以该指纹为键的新问题,要么将事件路由到已经绑定该指纹的问题。
custom-rule:<rule_id>
当问题反复出现时,应将两种方式结合使用:先把历史重复问题合并到目标问题,再创建规则。规则 API 不接受目标问题 ID——规则开始生效后,可以再将生成的 问题合并到同一目标,使整合结果保持有效。对于不预计再次发生的历史指纹泛滥问题,仅使用合并即可。对于一种尚未出现、需要提前处理的新模式,如果不需要与现有问题整合,则可仅使用分组规则。
custom-rule:...Workflow
工作流
Step 1 — Confirm the duplicates
步骤 1 — 确认重复项
Search by exception type or message to find candidates:
json
posthog:query-error-tracking-issues-list
{
"searchQuery": "TypeError: Cannot read property",
"status": "active",
"limit": 50,
"orderBy": "occurrences",
"dateRange": { "date_from": "-30d" }
}For each candidate, pull one sampled exception event to compare stack, type,
and message:
json
posthog:query-error-tracking-issue-events
{
"issueId": "<candidate_issue_id>",
"limit": 1,
"include": ["exception", "stacktrace", "environment"]
}Run this once per candidate. The tool defaults to , which
makes the top in-app frame stand out at a glance. If two candidates share the
same top frame and same exception type, they're likely the same error — but
verify against the full checklist below before merging.
onlyAppFrames: true按异常类型或消息搜索,以查找候选项:
json
posthog:query-error-tracking-issues-list
{
"searchQuery": "TypeError: Cannot read property",
"status": "active",
"limit": 50,
"orderBy": "occurrences",
"dateRange": { "date_from": "-30d" }
}对于每个候选项,抽取一个采样的异常事件,以比较堆栈、类型和消息:
json
posthog:query-error-tracking-issue-events
{
"issueId": "<candidate_issue_id>",
"limit": 1,
"include": ["exception", "stacktrace", "environment"]
}每个候选项运行一次。该工具默认使用 ,因此可以一眼看出最上层的应用内堆栈帧。如果两个候选项具有相同的顶层堆栈帧和相同的异常类型,它们很可能是同一错误——但在合并前,仍需对照下面的完整检查清单进行验证。
onlyAppFrames: trueAre they the same error?
它们是同一个错误吗?
Treat two issues as duplicates only when every one of these matches:
- is the same SDK. The browser/JS SDK captures
$libas$lib(notweb); server SDKs useposthog-js,posthog-python, etc. Confirm the exact value withposthog-node(read-data-schemaforevent_property_valueson$lib) rather than assuming — a wrong value silently matches nothing. Errors from different SDKs almost always come from different code paths even when the exception type matches.$exception - The exception type is identical ().
$exception_types - The top in-app stack frame points at the same file and same function. Line numbers and minor offsets within that function are fine; a different file or a different function on top means a different bug.
- The message follows the same template, with differences confined to volatile data — IDs, hashes, timestamps, dynamic paths. If the difference is a different verb, object, or operation, it's a different bug.
- agrees (both handled or both unhandled). A caught variant and an uncaught variant are different code paths and benefit from staying separate.
$exception_handled
If any single one of those differs, they are not duplicates — investigate
separately ().
investigating-error-issue只有在以下所有条件都匹配时,才能将两个问题视为重复项:
- 来自同一个 SDK。浏览器或 JS SDK 会将
$lib捕获为$lib(而不是web);服务端 SDK 则使用posthog-js、posthog-python等。应使用posthog-node(查询read-data-schema事件上$exception的$lib)确认准确值,而不能直接假设——错误的值会悄无声息地导致规则无法匹配。即使异常类型相同,来自不同 SDK 的错误也几乎总是来自不同的代码路径。event_property_values - 异常类型完全相同()。
$exception_types - 堆栈顶层的应用内堆栈帧指向同一个文件和同一个函数。函数内的行号及轻微偏移差异可以接受;如果顶层是不同文件或不同函数,则属于不同 bug。
- 消息遵循相同模板,差异仅限于易变数据,例如 ID、哈希、时间戳和动态路径。如果差异涉及不同的动词、对象或操作,则属于不同 bug。
- 一致,即两者要么都经过处理,要么都未经过处理。已捕获变体和未捕获变体属于不同代码路径,分别保留有助于准确定位问题。
$exception_handled
如果其中任何一项不同,它们就不是重复项——应分别调查()。
investigating-error-issueWhat NOT to group together
不应分组在一起的情况
These are the failure modes that destroy debugging signal. Do not group
across any of them, even when the user describes them as "the same kind of
bug":
- Frontend and backend variants of the same exception type. A from a browser bundle and a
TypeErrorfrom a Node service share a name and often a message word, but the stack, the runtime, and the fix all differ.TypeError - Different SDKs / platforms. (browser/JS) vs
webvsposthog-pythonare different call sites.posthog-node - Same type, different file or function on top of the stack. A
thrown from
NullPointerExceptionis not the same bug as one thrown fromOrderService.cancel, even if both messages say "user was null".PaymentService.refund - Caught vs uncaught. Two issues that differ only in are usually a code path that swallows the error in one place and lets it propagate in another — keeping them separate makes that visible.
$exception_handled - Conceptually-similar bugs that happen to share a phrase. "Cannot read property of undefined" appears in many independent bugs. Without matching stack frames, message similarity alone is not enough.
以下情况会破坏调试信号。即使用户将它们描述为“同一类 bug”,也不应跨越这些边界进行分组:
- 同一异常类型的前端和后端变体。 浏览器 bundle 中出现的 与 Node 服务中出现的
TypeError虽然名称相同,消息中也常包含相同词语,但它们的堆栈、运行时和修复方式都不同。TypeError - 不同 SDK 或平台。 (浏览器或 JS)与
web、posthog-python对应不同的调用位置。posthog-node - 类型相同,但堆栈顶层的文件或函数不同。 从 抛出的
OrderService.cancel与从NullPointerException抛出的异常不是同一个 bug,即使两条消息都写着“user was null”。PaymentService.refund - 已捕获与未捕获。 仅 不同的两个问题,通常意味着一个代码路径吞掉了错误,而另一个代码路径让错误继续传播;将它们分开可以保留这一重要区别。
$exception_handled - 碰巧包含相同短语的概念相似 bug。 “Cannot read property of undefined” 会出现在许多彼此独立的 bug 中。如果没有匹配的堆栈帧,仅凭消息相似度不足以判定为重复项。
Step 2 — Pick the target issue
步骤 2 — 选择目标问题
Pick the issue that should absorb the others:
- Most occurrences — keeps the dominant issue so dashboards stay continuous
- Best name and description — if the user has annotated one, prefer it
- Earliest — preserves the original timeline
first_seen
Note the target's ID. The other candidates become to merge in.
ids选择应吸收其他问题的问题:
- 发生次数最多——保留主导问题,使 dashboard 保持连续。
- 名称和描述最佳——如果用户为某个问题添加过标注,应优先选择它。
- 最早——保留原始时间线。
first_seen
记下目标问题的 ID。其他候选项的 ID 将作为待合并的 。
idsStep 3 — Merge existing duplicates
步骤 3 — 合并现有重复项
json
posthog:error-tracking-issues-merge-create
{
"id": "<target_issue_id>",
"ids": ["<duplicate_id_1>", "<duplicate_id_2>", "..."]
}Merge is destructive (annotation ) — once issues are merged
into a target, the source issues are gone from the active list. Confirm the
target with the user before calling. Cap each merge call at ~50 source IDs to
keep failures localized; for larger sprawl, batch.
destructive: trueMerged changes may not appear in the issue list immediately — re-listing right
after the call can still show the source issues for a short window. If a
follow-up call looks unchanged, wait a few seconds
and re-query rather than re-issuing the merge.
error-tracking-issues-listIf after the merge the target's metadata looks wrong (a duplicate had a better
name), use to fix the name or description
on the target rather than re-merging.
error-tracking-issues-partial-updatejson
posthog:error-tracking-issues-merge-create
{
"id": "<target_issue_id>",
"ids": ["<duplicate_id_1>", "<duplicate_id_2>", "..."]
}合并是破坏性操作(注解 )——问题合并到目标后,源问题会从活跃列表中消失。调用前应向用户确认目标。每次合并调用应限制在约 50 个源问题 ID 内,以便在失败时准确定位;数量更多时应分批处理。
destructive: true合并后的变更可能不会立即出现在问题列表中——调用后立即重新列出时,源问题仍可能在短时间内显示。如果后续的 调用看起来没有变化,应等待几秒后重新查询,而不是再次发起合并。
error-tracking-issues-list如果合并后目标问题的元数据不正确,例如某个重复问题原本有更好的名称,应使用 修正目标问题的名称或描述,而不是再次合并。
error-tracking-issues-partial-updateStep 4 — Decide if a grouping rule is warranted
步骤 4 — 判断是否需要分组规则
A grouping rule is worth creating when both are true:
- The pattern keeps producing new fingerprints (you have seen new duplicates appear since the last merge)
- You can describe the pattern with property filters that won't accidentally swallow unrelated errors
The canonical exception properties (,
for messages, for file paths, for
function names) are arrays at capture time. PostHog's property filters
special-case them — each filter matches against the individual array
elements, so all the standard operators (, , ,
, , ) work with the bare value:
, not or .
$exception_types$exception_values$exception_sources$exception_functionsexactis_noticontainsnot_icontainsregexnot_regexexact "TypeError"exact '["TypeError"]'regex '"TypeError"'The singular forms (, ) and
are emitted on a fraction of a percent of events;
filtering on them produces a rule that silently never matches.
$exception_type$exception_message$exception_stack_trace_rawIf the volatility is in the message (e.g.,
), a regex filter on
works. If the volatility is in line numbers within a known file,
on does. is also a useful narrowing
dimension — separate handled vs unhandled rather than mixing them.
TypeError at /static/main.<hash>.js$exception_valuesicontains$exception_sources$exception_handledSkip the grouping rule when:
- The duplicates are historical (one-off backfill, no new occurrences) — merge is enough
- You can't write a filter narrow enough to be safe — broaden the merge cadence instead and revisit later
只有在以下两个条件同时成立时,才值得创建分组规则:
- 该模式仍会持续产生新指纹,即上次合并后已经出现了新的重复项。
- 可以使用不会意外吞并无关错误的属性过滤器来描述该模式。
标准异常属性(用于消息的 、用于文件路径的 、用于函数名的 )在捕获时均为数组。PostHog 的属性过滤器会对其进行特殊处理——每个过滤器都会匹配数组中的各个元素,因此所有标准操作符(、、、、、)都可以直接使用裸值,例如 ,而不是 或 。
$exception_values$exception_sources$exception_functionsexactis_noticontainsnot_icontainsregexnot_regexexact "TypeError"exact '["TypeError"]'regex '"TypeError"'单数形式(、)以及 只会出现在不到百分之几的事件中;使用这些属性进行过滤会创建一条永远不会匹配任何事件的规则。
$exception_type$exception_message$exception_stack_trace_raw如果易变数据位于消息中,例如 ,可以在 上使用正则过滤器。如果易变数据位于已知文件内的行号中,则可以在 上使用 。 也可用于进一步缩小范围——应将已处理与未处理的错误分开,而不是混在一起。
TypeError at /static/main.<hash>.js$exception_values$exception_sourcesicontains$exception_handled以下情况不要创建分组规则:
- 重复项仅存在于历史数据中,例如一次性回填且没有新发生的情况——仅合并即可。
- 无法编写足够狭窄且安全的过滤器——应改为扩大合并范围并提高合并频率,之后再重新评估。
Step 5 — Create the grouping rule
步骤 5 — 创建分组规则
Translate the step 1 "same error" checklist into rule filters. A rule that
matches more loosely than the checklist will silently merge unrelated bugs
forever — the rule is more dangerous than the merge because it runs against
every future event. At a minimum, scope by SDK and exception type, and add
a third dimension (file path via , or a specific message
phrase via ) to pin the call site.
$exception_sources$exception_valuesConfirm the value first — the browser/JS SDK captures , not
, so a rule filtering on silently never matches. Verify
with ( for on )
before baking a value into the rule:
$lib$lib="web"posthog-jsposthog-jsread-data-schemaevent_property_values$lib$exceptionjson
posthog:error-tracking-grouping-rules-create
{
"filters": {
"type": "AND",
"values": [
{
"type": "event",
"key": "$lib",
"operator": "exact",
"value": "web"
},
{
"type": "event",
"key": "$exception_types",
"operator": "exact",
"value": "TypeError"
},
{
"type": "event",
"key": "$exception_sources",
"operator": "icontains",
"value": "/static/checkout/"
},
{
"type": "event",
"key": "$exception_values",
"operator": "icontains",
"value": "Cannot read property"
}
]
},
"description": "Cleanup: collapse noisy checkout TypeError fingerprints (web)"
}Rules are evaluated in order. List existing rules first
() — if a rule already partially
covers the pattern, prefer adjusting its filter over stacking a near-duplicate.
posthog:error-tracking-grouping-rules-listThe optional field auto-assigns issues created by the rule. Skip it
unless the user explicitly wants ownership baked into the rule.
assignee将步骤 1 中的“同一错误”检查清单转换为规则过滤器。比检查清单更宽松的规则会永远静默合并无关 bug——规则比合并更危险,因为它会作用于每一个未来事件。至少应按 SDK 和异常类型限定范围,并增加第三个维度,例如通过 限定文件路径,或通过 限定特定消息短语,以锁定调用位置。
$exception_sources$exception_values首先确认 值——浏览器或 JS SDK 捕获的是 ,而不是 ,因此过滤 的规则永远不会匹配。在将某个值写入规则前,应使用 (查询 事件上 的 )进行验证:
$lib$lib="web"posthog-jsposthog-jsread-data-schema$exception$libevent_property_valuesjson
posthog:error-tracking-grouping-rules-create
{
"filters": {
"type": "AND",
"values": [
{
"type": "event",
"key": "$lib",
"operator": "exact",
"value": "web"
},
{
"type": "event",
"key": "$exception_types",
"operator": "exact",
"value": "TypeError"
},
{
"type": "event",
"key": "$exception_sources",
"operator": "icontains",
"value": "/static/checkout/"
},
{
"type": "event",
"key": "$exception_values",
"operator": "icontains",
"value": "Cannot read property"
}
]
},
"description": "Cleanup: collapse noisy checkout TypeError fingerprints (web)"
}规则按顺序执行。应先列出现有规则()——如果已有规则部分覆盖该模式,应优先调整其过滤器,而不是堆叠一条近乎重复的规则。
posthog:error-tracking-grouping-rules-list可选的 字段会自动为规则创建的问题分配负责人。除非用户明确希望将所有权固化在规则中,否则应跳过该字段。
assigneeStep 6 — Verify and consolidate
步骤 6 — 验证并整合
Sample the merged issue's recent events to confirm the merge succeeded.
Watch for the rule's fingerprint to start matching
events — the first match creates a new issue (or routes to whatever was
already bound to that fingerprint). To keep events under your historical
target rather than scattered across the new custom-rule issue, run a second
merge folding the custom-rule issue into the target.
custom-rule:<rule_id>If new (non-rule) fingerprints continue appearing despite the rule, its
filter is too narrow — widen it.
抽取合并后问题的近期事件,以确认合并是否成功。观察规则的 指纹是否开始匹配事件——首次匹配会创建一个新问题,或将事件路由到已经绑定该指纹的问题。为了让事件继续归属于历史目标,而不是分散到新的 custom-rule 问题中,应再执行一次合并,将 custom-rule 问题并入目标问题。
custom-rule:<rule_id>如果规则生效后仍持续出现新的非规则指纹,说明其过滤器过于狭窄——应扩大过滤范围。
Tips
提示
- The user often confuses grouping rules with assignment rules. Grouping rules decide which issue an event lands in. Assignment rules decide who owns the resulting issue.
- Don't merge issues that "look similar" without inspecting events. Two
s in different files are different bugs.
TypeError - Stack frames are the canonical grouping signal — ingestion already fingerprints on the stack, so a stable stack groups itself. A grouping rule is for cases where the natural fingerprint sprays (volatile filenames, hashed function names, dynamic line numbers) and you need to override it.
- Disabling or tightening a grouping rule does not retroactively un-group
existing events; future events route correctly, past events stay where they
are. Use if you need to surgically separate fingerprints back out of a merged issue.
error-tracking-issues-split-create - Grouping rules are visible in the UI under Project settings → Error tracking → Grouping rules; mention this when the user asks where rules live.
- 用户经常混淆分组规则和分配规则。分组规则决定事件进入哪个问题,分配规则决定生成的问题由谁负责。
- 不要在未检查事件的情况下合并“看起来相似”的问题。位于不同文件中的两个 是不同的 bug。
TypeError - 堆栈帧是标准分组信号——数据摄取时已经根据堆栈生成指纹,因此稳定的堆栈会自行归组。分组规则适用于自然指纹发生扩散的情况,例如文件名、哈希函数名或动态行号不稳定,需要手动覆盖指纹。
- 禁用或收紧分组规则不会追溯性地取消已有事件的分组;未来事件会正确路由,而过去的事件仍留在原处。如需从已合并问题中精确分离指纹,可使用 。
error-tracking-issues-split-create - 分组规则可在 UI 的 Project settings → Error tracking → Grouping rules 中查看;当用户询问规则所在位置时,应告知这一路径。
Related skills
相关 skill
- — drop worthless events entirely instead of regrouping them
suppressing-noisy-errors - — re-rank the issue queue once grouping is cleaned up
triaging-error-issues - — inspect a merged issue end to end
investigating-error-issue
- ——直接丢弃无价值的事件,而不是对其重新分组。
suppressing-noisy-errors - ——完成分组清理后,重新排列问题队列。
triaging-error-issues - ——从头到尾检查合并后的问题。
investigating-error-issue