suppressing-noisy-errors
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSuppressing noisy errors
抑制无意义错误
Suppression is destructive in spirit: matching events are dropped at ingestion and
never become issues. The wrong rule silently throws away real bugs. This skill
exists to make sure suppression is applied only to patterns that are genuinely
unactionable, with filters narrow enough to avoid swallowing unrelated errors.
从本质上来说,抑制是具有破坏性的:匹配到的事件会在摄入阶段被丢弃,永远不会成为待处理问题。错误的规则会悄无声息地丢掉真实的Bug。此功能的存在是为了确保抑制仅应用于真正无法处理的错误模式,且过滤器的范围足够狭窄,避免误吞无关错误。
When suppression is the right tool
何时适合使用抑制功能
Suppression is the right tool when an error is:
- Unactionable from your code — browser extensions, third-party scripts, ad blockers, network beacons firing after navigation. You can't fix it because you didn't write it.
- Browser engine quirks — ,
ResizeObserver loop limit exceeded,Script error.with empty payloads.Non-Error promise rejection captured - Bot or crawler traffic — errors firing only from headless browsers or known crawler user agents.
- Sampling already enough — for high-volume but real errors, dampen with
instead of full suppression so you keep visibility without paying full cost.
sampling_rate
Suppression is not the right tool when:
- The error is unactionable today but might become actionable after a fix —
use issue status or
archivedinstead so it surfaces if it returns.resolved - You only want to mute notifications — assign the issue to a user, change its status, or use notification rules.
- The error is a duplicate of another — merge or create a grouping rule
().
grouping-noisy-errors
当错误符合以下特征时,抑制是合适的工具:
- 代码层面无法处理——浏览器扩展、第三方脚本、广告拦截器、导航后触发的网络信标。你无法修复这些错误,因为它们并非由你编写。
- 浏览器引擎特性问题——、
ResizeObserver loop limit exceeded、负载为空的Script error.。Non-Error promise rejection captured - 机器人或爬虫流量——仅在无头浏览器或已知爬虫用户代理中触发的错误。
- 抽样已足够——对于高数量但真实存在的错误,使用降低数据量而非完全抑制,这样既能保持可见性,又无需支付全额成本。
sampling_rate
在以下场景中,不适合使用抑制功能:
- 错误目前无法处理,但修复后可能变得可处理——改用问题状态或
archived,这样如果错误再次出现,会重新显示。resolved - 你只想关闭通知——将问题分配给用户、更改其状态,或使用通知规则。
- 错误是另一个错误的重复项——合并错误或创建分组规则()。
grouping-noisy-errors
Available tools
可用工具
| Tool | Purpose |
|---|---|
| Find suppression candidates by volume and impact; dry-run a candidate filter via |
| Inspect sampled |
| Fallback dry-run for filters that need OR groups or operators outside the |
| Check existing suppression rules |
| Create the suppression rule |
| Hide past data via issue status without dropping events at ingestion |
| 工具名称 | 用途 |
|---|---|
| 通过数量和影响程度识别适合抑制的错误候选;通过 |
| 查看抽样的 |
| 当过滤器需要OR组或 |
| 查看已有的抑制规则 |
| 创建抑制规则 |
| 通过修改问题状态隐藏历史数据,而非在摄入阶段丢弃事件 |
Workflow
工作流程
Step 1 — Identify candidates
步骤1——识别候选错误
High occurrences with low distinct users is the strongest noise signal — one
user (or one bot) producing many events.
json
posthog:query-error-tracking-issues-list
{
"status": "active",
"orderBy": "occurrences",
"orderDirection": "DESC",
"dateRange": { "date_from": "-7d" },
"limit": 30,
"volumeResolution": 0
}Look for:
- High , low
occurrencesratio (e.g., 50,000 occurrences, 3 users → likely bot or extension loop)users - Exception messages matching known noise patterns: ,
ResizeObserver loop, extension namespaces (Script error.,chrome-extension://,moz-extension://)safari-extension:// - Stack traces dominated by third-party domains the user doesn't control
高出现次数但低独立用户数是最明显的无意义错误信号——单个用户(或单个机器人)产生大量事件。
json
posthog:query-error-tracking-issues-list
{
"status": "active",
"orderBy": "occurrences",
"orderDirection": "DESC",
"dateRange": { "date_from": "-7d" },
"limit": 30,
"volumeResolution": 0
}重点关注:
- 高、低
occurrences的比例(例如,50000次出现,仅3个用户→可能是机器人或扩展循环)users - 异常消息匹配已知的无意义模式:、
ResizeObserver loop、扩展命名空间(Script error.、chrome-extension://、moz-extension://)safari-extension:// - 堆栈跟踪主要由用户无法控制的第三方域名主导
Step 2 — Confirm the pattern
步骤2——确认错误模式
For each candidate, pull a sample of events and check that the
pattern matches what you intend to suppress:
$exceptionjson
posthog:query-error-tracking-issue-events
{
"issueId": "<candidate_issue_id>",
"limit": 10,
"verbosity": "stack"
}onlyAppFramestrueonlyAppFrames: falseConfirm:
- The exception type or message text is consistent across the sample
- The URLs / user agents / browsers don't include real user traffic mixed in with the noise
- Suppressing this pattern won't hide a future real bug that happens to share the type
If any sample doesn't match, narrow the filter or skip the candidate.
对于每个候选错误,抽取事件样本,检查模式是否与你想要抑制的内容匹配:
$exceptionjson
posthog:query-error-tracking-issue-events
{
"issueId": "<candidate_issue_id>",
"limit": 10,
"verbosity": "stack"
}onlyAppFramestrueonlyAppFrames: false确认:
- 样本中的异常类型或消息文本保持一致
- URL/用户代理/浏览器中没有混入真实用户流量
- 抑制此模式不会隐藏未来可能出现的、恰好共享相同类型的真实Bug
如果任何样本不匹配,缩小过滤器范围或跳过该候选错误。
Step 3 — Scope the filter tightly
步骤3——精准限定过滤器范围
Suppression rules are configured with the same filter shape as grouping rules.
The tool description warns explicitly:
do not create match-all rules and do not create overly broad rules.
Match on the most specific property combination you can:
error-tracking-suppression-rules-create| Noise pattern | Recommended filter |
|---|---|
| Chrome extension errors | |
| Firefox extension errors | |
| Safari extension errors | |
| ResizeObserver loop | |
| Cross-origin "Script error." | |
| Bot user agents | |
| Third-party network beacon failures | |
The canonical exception properties (, ,
, ) are arrays at capture time. The
property filter compiler special-cases them — it parses the
JSON-materialized column and wraps the filter in
, so all the standard operators
(, , , , , ) work
against individual elements with the bare value: , not
or .
$exception_types$exception_values$exception_sources$exception_functionsarrayExists(v -> ..., JSONExtract(...))exactis_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_rawNote that the operator on suppression and grouping rules compiles to
the HogVM , which is case-sensitive. Use the
inline flag for case-insensitive matching (e.g. ).
regexOperation::Regex(?i)(?i)headlesschromeFor matching multiple bot or crawler terms, use bare pipes for alternation.
Pass this as the field of the regex filter when calling the API
( is more reliable than the parsed , which some
parsers normalize away from crawler markers):
value$raw_user_agent$user_agenttext
(?i)(HeadlessChrome|bot|crawler|spider)Whenever possible, AND together two or more conditions — type plus message, or
message plus URL pattern — so the rule is specific to the real noise.
抑制规则使用与分组规则相同的过滤器结构。工具的描述明确警告:不要创建匹配所有内容的规则,也不要创建过于宽泛的规则。尽可能匹配最具体的属性组合:
error-tracking-suppression-rules-create| 无意义错误模式 | 推荐过滤器 |
|---|---|
| Chrome扩展错误 | |
| Firefox扩展错误 | |
| Safari扩展错误 | |
| ResizeObserver循环错误 | |
| 跨域"Script error." | |
| 机器人用户代理 | |
| 第三方网络信标失败 | |
标准异常属性(、、、)在捕获时为数组。属性过滤器编译器对这些属性做了特殊处理——它会解析JSON物化列,并将过滤器包装在中,因此所有标准运算符(、、、、、)都可以针对单个元素使用原始值:,而非或。
$exception_types$exception_values$exception_sources$exception_functionsarrayExists(v -> ..., JSONExtract(...))exactis_noticontainsnot_icontainsregexnot_regexexact "TypeError"exact '["TypeError"]'regex '"TypeError"'单数形式(、)和仅在极少比例的事件中出现;基于这些属性过滤会创建一个永远无法匹配的规则。
$exception_type$exception_message$exception_stack_trace_raw请注意,抑制和分组规则中的运算符会编译为HogVM的,这是区分大小写的。如需不区分大小写的匹配,可使用内联标志(例如)。
regexOperation::Regex(?i)(?i)headlesschrome如需匹配多个机器人或爬虫术语,使用竖线进行交替。调用API时,将此作为regex过滤器的字段(比解析后的更可靠,因为某些解析器会将爬虫标记标准化掉):
value$raw_user_agent$user_agenttext
(?i)(HeadlessChrome|bot|crawler|spider)尽可能将两个或多个条件通过AND组合——类型加消息,或消息加URL模式——确保规则仅针对真实的无意义错误。
Step 4 — Decide: suppress or sample
步骤4——决策:抑制还是抽样
If you want to keep some visibility, use between 0 and 1:
sampling_rate- — drop everything matching (full suppression)
sampling_rate: 1 - — drop 95% of matching events, keep 5% as sentinel data
sampling_rate: 0.95 - — half-rate, useful for high-volume but real errors
sampling_rate: 0.5
Default to a non-1.0 sampling rate when there's any doubt that the pattern is
purely noise. You can tighten to 1.0 later once the data shows the rule isn't
catching real issues.
如果你想保留一定的可见性,可将设置在0到1之间:
sampling_rate- ——丢弃所有匹配的事件(完全抑制)
sampling_rate: 1 - ——丢弃95%的匹配事件,保留5%作为哨兵数据
sampling_rate: 0.95 - ——减半保留,适用于高数量但真实存在的错误
sampling_rate: 0.5
当对模式是否完全是无意义错误存在任何疑问时,默认使用非1.0的抽样率。一旦数据显示规则未捕获真实问题,可随后将其调整为1.0。
Step 5 — Dry-run the filter against live data
步骤5——针对实时数据试运行过滤器
Before asking for confirmation, run the candidate filter against the issues
list so you (and the user) can see exactly which issues the rule would have
caught over the last 7 days. accepts the
same property-filter shape suppression rules use via its
parameter, so for a typical AND-only rule you can pass the rule's leaf
filters directly — no HogQL translation needed:
query-error-tracking-issues-listfilterGroupjson
posthog:query-error-tracking-issues-list
{
"filterGroup": [
{ "type": "event", "key": "$exception_types", "operator": "exact", "value": "Error" },
{ "type": "event", "key": "$exception_values", "operator": "icontains", "value": "ResizeObserver loop" }
],
"dateRange": { "date_from": "-7d" },
"status": "all",
"filterTestAccounts": false,
"orderBy": "occurrences",
"limit": 25
}Important defaults to override for suppression preview:
- — suppression applies regardless of issue status, so don't let the default
status: "all"filter hide already-archived noise.active - — the rule will not respect the test-account toggle at ingestion. The preview should match production reality.
filterTestAccounts: false
Each row is one issue the rule would catch: (exception type),
(sample message), , , plus
and . The issue list is
the per-issue breakdown — read every row.
namedescriptionsourcelibraryaggregations.occurrencesaggregations.usersThe single most important safety check: scan the result for any issue
whose / / looks like a real bug the team
would want to fix, not noise. A filter that looks tight by message text
will routinely match unrelated issues that happen to share a phrase, and
this is the failure mode that silently destroys real data once the rule is
live. If you see anything suspicious, narrow the filter (step 3) and rerun
this step until only the genuine noise pattern is in the list.
namedescriptionsourceAdd up and across rows for
the blast-radius totals you'll surface to the user in step 6. If you need
exact totals across more than issues, paginate with or
fall back to the HogQL aggregate at the end of this step.
aggregations.occurrencesaggregations.userslimitoffsetFor one or two concrete sample events with full stack traces, follow up on
the most suspicious-looking issue with :
query-error-tracking-issue-eventsjson
posthog:query-error-tracking-issue-events
{
"issueId": "<id from the list>",
"limit": 3,
"verbosity": "stack",
"onlyAppFrames": false
}在请求确认之前,针对问题列表运行候选过滤器,这样你(和用户)可以清楚地看到规则在过去7天内会捕获哪些问题。通过参数接受与抑制规则相同的属性过滤器结构,因此对于典型的仅AND规则,你可以直接传递规则的叶子过滤器——无需转换为HogQL:
query-error-tracking-issues-listfilterGroupjson
posthog:query-error-tracking-issues-list
{
"filterGroup": [
{ "type": "event", "key": "$exception_types", "operator": "exact", "value": "Error" },
{ "type": "event", "key": "$exception_values", "operator": "icontains", "value": "ResizeObserver loop" }
],
"dateRange": { "date_from": "-7d" },
"status": "all",
"filterTestAccounts": false,
"orderBy": "occurrences",
"limit": 25
}针对抑制预览需要覆盖的重要默认值:
- ——抑制适用于所有状态的问题,因此不要让默认的
status: "all"过滤器隐藏已归档的无意义错误。active - ——规则在摄入阶段不会遵循测试账户开关。预览应与生产环境实际情况一致。
filterTestAccounts: false
每一行代表规则会捕获的一个问题:(异常类型)、(样本消息)、、,以及和。问题列表就是按问题拆分的结果——请逐行查看。
namedescriptionsourcelibraryaggregations.occurrencesaggregations.users最重要的安全检查:扫描结果,查看是否有任何问题的//看起来像是团队想要修复的真实Bug,而非无意义错误。仅通过消息文本看起来范围狭窄的过滤器,通常会匹配恰好包含相同短语的无关问题,这是规则生效后会悄无声息破坏真实数据的故障模式。如果发现任何可疑内容,缩小过滤器范围(步骤3)并重新运行此步骤,直到列表中仅包含真正的无意义错误模式。
namedescriptionsource将各行的和相加,得到步骤6中需要向用户展示的影响范围总计。如果需要超过个问题的精确总计,可使用分页,或退回到此步骤末尾的HogQL聚合查询。
aggregations.occurrencesaggregations.userslimitoffset如需查看带有完整堆栈跟踪的一两个具体事件样本,对看起来最可疑的问题使用进行跟进:
query-error-tracking-issue-eventsjson
posthog:query-error-tracking-issue-events
{
"issueId": "<id from the list>",
"limit": 3,
"verbosity": "stack",
"onlyAppFrames": false
}When you must fall back to execute-sql
何时必须退回到execute-sql
filterGroup- The rule uses at the outer group or any nested OR.
type: "OR" - The rule uses operators not supported by (e.g.
filterGroup,between,in).semver_* - You want a precise event-level count rather than per-issue aggregates.
The HogQL shape mirrors what the suppression rule bytecode compiles to.
The materialized property column is nullable, so the
wrapper is required — without it ClickHouse rejects the query with
"Nested type Array(String) cannot be inside Nullable type":
coalesce(..., '[]')sql
SELECT
count() AS matched,
count(DISTINCT distinct_id) AS users,
count(DISTINCT properties.$exception_issue_id) AS issues
FROM events
WHERE event = '$exception'
AND timestamp > now() - INTERVAL 7 DAY
AND arrayExists(
v -> ifNull(ilike(v, '<pattern>'), 0),
JSONExtract(coalesce(properties.$exception_values, '[]'), 'Array(String)')
)Use for , plain equality for , for . The rule's is case-sensitive — add
inline if needed.
ilikeicontainsexactmatch(v, '<pattern>')regexregex(?i)filterGroup- 规则在外部组或任何嵌套组中使用。
type: "OR" - 规则使用不支持的运算符(例如
filterGroup、between、in)。semver_* - 你需要精确的事件级计数,而非按问题聚合的计数。
HogQL的结构与抑制规则字节码编译后的结构一致。物化属性列可为空,因此需要包装——否则ClickHouse会拒绝查询并提示“Nested type Array(String) cannot be inside Nullable type”:
coalesce(..., '[]')sql
SELECT
count() AS matched,
count(DISTINCT distinct_id) AS users,
count(DISTINCT properties.$exception_issue_id) AS issues
FROM events
WHERE event = '$exception'
AND timestamp > now() - INTERVAL 7 DAY
AND arrayExists(
v -> ifNull(ilike(v, '<pattern>'), 0),
JSONExtract(coalesce(properties.$exception_values, '[]'), 'Array(String)')
)使用对应,使用普通等式对应,使用对应。规则的是区分大小写的——如需不区分大小写,可添加内联。
ilikeicontainsexactmatch(v, '<pattern>')regexregex(?i)Step 6 — Confirm with the user before creating
步骤6——创建前与用户确认
Suppression is destructive in spirit even though the API marks it
. Show the user before creating:
destructive: false- The exact filter you plan to send
- The list of issues from step 5 with their and
occurrences, plus the aggregate totals — call out any rows that look like real bugsusers - Whether it overlaps any existing suppression rules
(first)
posthog:error-tracking-suppression-rules-list
Wait for explicit confirmation. Then create:
json
posthog:error-tracking-suppression-rules-create
{
"filters": {
"type": "AND",
"values": [
{
"type": "event",
"key": "$exception_types",
"operator": "exact",
"value": "Error"
},
{
"type": "event",
"key": "$exception_values",
"operator": "icontains",
"value": "ResizeObserver loop"
}
]
},
"sampling_rate": 0.95
}Start at (drop 95%, keep 5% as sentinel data) so you can confirm the
rule isn't catching real errors before tightening to .
0.951.0尽管API标记抑制为,但从本质上来说它是具有破坏性的。创建前向用户展示以下内容:
destructive: false- 你计划发送的精确过滤器
- 步骤5中的问题列表及其和
occurrences,以及总计——特别指出任何看起来像真实Bug的行users - 是否与现有抑制规则重叠(先调用)
posthog:error-tracking-suppression-rules-list
等待用户明确确认后,再创建规则:
json
posthog:error-tracking-suppression-rules-create
{
"filters": {
"type": "AND",
"values": [
{
"type": "event",
"key": "$exception_types",
"operator": "exact",
"value": "Error"
},
{
"type": "event",
"key": "$exception_values",
"operator": "icontains",
"value": "ResizeObserver loop"
}
]
},
"sampling_rate": 0.95
}初始设置为(丢弃95%,保留5%作为哨兵数据),这样你可以在将其调整为之前,确认规则未捕获真实错误。
0.951.0Step 7 — Watch the rule for 24-48h
步骤7——监控规则24-48小时
After creating the rule:
- Confirm matching events are no longer being captured by running the same
filter against a short window scoped to after the rule was created
(e.g. once an hour has passed). Don't re-run the 7-day estimate from step 5 — suppression only applies to new events, so historical events in the window will still be there and the count won't drop.
WHERE timestamp > now() - INTERVAL 1 HOUR - Watch related active issues over the post-creation window — if their volume drops while non-related issues hold steady, the rule was scoped correctly
- If a related real issue's volume drops too (false-positive), ask the user to
disable the rule via Project settings → Error tracking → Suppression rules
immediately and tighten the filter before re-creating it. The MCP tools to
edit or delete a rule (,
error-tracking-suppression-rules-partial-update) are not enabled — the agent has no way to recover programmatically.-destroy
If you see signs of false positives (a real issue going quiet at the same time
the rule was created), prefer disabling the rule over deleting it — that
preserves the rule's configuration for forensic review.
创建规则后:
- 通过针对规则创建后的短时间窗口(例如规则创建1小时后,使用)运行相同的过滤器,确认匹配的事件不再被捕获。不要重新运行步骤5中的7天估算——抑制仅适用于新事件,因此窗口内的历史事件仍会存在,计数不会下降。
WHERE timestamp > now() - INTERVAL 1 HOUR - 在规则创建后的窗口内监控相关的活跃问题——如果这些问题的数量下降,而非相关问题保持稳定,则说明规则范围设置正确
- 如果相关真实问题的数量也下降(误判),请立即让用户通过项目设置→错误追踪→抑制规则禁用该规则,并在重新创建前缩小过滤器范围。MCP的规则编辑或删除工具(、
error-tracking-suppression-rules-partial-update)未启用——代理无法通过编程方式恢复数据。-destroy
如果发现误判迹象(规则创建后某个真实问题不再出现),优先禁用规则而非删除——这样可以保留规则配置用于取证审查。
Tips
提示
- Project settings → Error tracking → Suppression rules shows the same data; mention this when the user asks where rules live in the UI.
- Suppression applies at ingestion. Existing issues from past events keep their data; only new events are dropped.
- For a status-only change (don't drop the data, just hide it from the active
list), prefer with
error-tracking-issues-partial-updateover a suppression rule.status: "suppressed" - The schema explicitly warns the model not to create match-all rules. If the
user asks "suppress everything from extensions", still scope by stack trace or
URL — never leave empty.
filters - A suppression rule that turns out to be too narrow is harmless (some noise leaks through). A rule that's too broad silently destroys real data — bias toward narrow.
- 项目设置→错误追踪→抑制规则会显示相同的数据;当用户询问规则在UI中的位置时,请提及此路径。
- 抑制在摄入阶段生效。过去事件产生的现有问题会保留其数据;仅新事件会被丢弃。
- 如需仅更改状态(不丢弃数据,仅从活跃列表中隐藏),优先使用并设置
error-tracking-issues-partial-update,而非创建抑制规则。status: "suppressed" - 架构明确警告模型不要创建匹配所有内容的规则。如果用户要求“抑制所有扩展的错误”,仍需通过堆栈跟踪或URL限定范围——永远不要留空。
filters - 范围过窄的抑制规则是无害的(部分无意义错误会漏过)。范围过宽的规则会悄无声息地破坏真实数据——倾向于使用范围更窄的规则。",