npx skills add ...
npx skills add signoz/agent-skills --skill signoz-investigating-alerts
npx skills add signoz/agent-skills --skill signoz-investigating-alerts
Diagnose why a SigNoz alert fired by correlating the alert's own signal with neighbor signals (error rate, latency, throughput, CPU/memory), traces, and logs around the fire window — and rank likely causes. Make sure to use this skill whenever the user asks "why did this alert fire", "what caused alert X", "investigate this alert", "RCA for the alert that paged me", "what's wrong with [service]" in the context of a recent fire, or otherwise asks for a root-cause analysis of a firing or recently-fired alert. Read-only — does not modify any alert or notification.
Diagnose why a SigNoz alert fired. The skill correlates the alert's own
signal with neighbor signals around the fire window, and surfaces a
ranked list of likely causes with supporting evidence. It is the
companion to signoz-explaining-alerts — explain decodes the rule
statically; investigate diagnoses a specific incident.
This skill calls SigNoz MCP server tools heavily (signoz_get_alert,
signoz_get_alert_history, signoz_execute_builder_query,
signoz_query_metrics, signoz_search_traces, signoz_search_logs,
signoz_get_trace_details, etc.). Before running the workflow,
confirm the signoz_* tools are available. If they are not, the
SigNoz MCP server is not installed or configured — run signoz-mcp-setup first
to initialize or repair the MCP connection. The investigation depends on
correlating multiple MCP queries; without the server there is no way to ground
the analysis.
Use this skill when the user wants to:
Do NOT use when the user wants to:
signoz-explaining-alerts.signoz-creating-alerts.signoz_update_alert directly.signoz-generating-queries.| Input | Required | Source if missing |
|---|---|---|
| Alert identifier (rule ID or name) | yes | $ARGUMENTS[0] or recent context |
| Time window | no | default to most recent fire from signoz_get_alert_history |
If the alert name is fuzzy, this skill is best-effort (read-only):
signoz_list_alert_rules, paginate, fuzzy-match the name.If no firing transition exists in the queried lookback window, stop: there is nothing to investigate. Respond with:
"Alert '[name]' has not fired in the last 7d, so there is no fire window to investigate. Use
signoz-explaining-alertsto walk through the rule, or check whether the alert is enabled."
The investigation runs in three tiers with strict early-stop gates. Tier 1 always runs. Tier 2 runs only if tier 1 confirms a real fire. Tier 3 runs only if tier 2 surfaces correlated anomalies. Skipping the gates produces hundreds of unnecessary trace/log queries on quiet alerts.
signoz_list_alert_rules (paginated) if
not given.signoz_get_alert for the full rule config — needed to know
what query, threshold, and resource scope the alert evaluated.signoz_get_alert_history with timeRange: "7d" and
order: "desc"; omit state so the timeline includes firing and inactive
transitions. Continue only when data.nextCursor exists (the completeness
note also reports hasMore: true). Pass it as cursor, replace timeRange
with the note's resolved absolute start and end, and preserve the same
state/filter (including omission) and order. Stop when nextCursor is absent
/ the note reports hasMore: false; never use offset or page fullness.
If a later intentional state filter means "resolved" / "recovered", use
inactive. The enum is inactive|pending|recovering|firing|nodata|disabled;
recovering is a transient keep-firing state, not resolution.
Pattern analysis needs the complete transition set. Rows are emitted per
label-group fingerprint; do not interleave them. From the response:
overallStateChanged: true: an overallState: "firing" transition opens
an incident; the next overallState: "inactive" closes it. Deduplicate
matching timestamps and sort by unixMilli ascending before pairing.
Default to the most recent incident unless $ARGUMENTS[1] selects another.fingerprint and retain each row's
labels. Use only stateChanged: true rows to decide when that group fired
and resolved, and which group should scope Tier 1–3 queries.one-off → single fire with a long quiet period before/after.sustained → fires that stayed firing for ≥ 1 evaluation cycle.flapping → ≥ 3 fires within a 1h window, alternating fire/resolve.recurring → fires at regular intervals (cron-like, e.g., every hour).This tier always runs. It establishes the fire is real (vs. transient threshold tickle or flap) and quantifies the magnitude.
[fire_start - 30m, fire_start + 30m].
signoz_execute_builder_query for the alert's stored builder,
formula, PromQL, or ClickHouse query envelope.builder_query leaves and 100 on standalone/formula results. Find leaves from every formula expression, including disabled: true formulas, following references through the dependency graph.
This walk sets comparison bounds only; it does not prove deterministic formula-to-formula order. Use v5 order: __result desc for metrics/formulas or primary aggregation desc for logs/traces, never dashboard orderBy.
Time-series top-N ranks over the whole window and may omit a short-lived local spike.(peak - threshold) / threshold * 100 for "above" alerts, inverted for "below".Run only if Tier 1 confirms a real breach. Pull related signals for the same resource scope as the alert and compare the fire window to a baseline window.
Pick a baseline window. Use the same hour, previous day
(fire_start - 24h, fire_start - 24h + fire_duration). If the
alert fired during a known-anomalous time (deploy, weekly job),
note it in the output but still proceed.
Look up neighbor signals for the alert's resource type. See
references/neighbor-signals.md for the lookup table. Common cases:
service.name = X): pull error rate,
p95/p99 latency, request throughput, dependency error rates if
trace data is available.host.name = X): CPU, memory, disk I/O,
network I/O.For each neighbor signal:
signoz_execute_builder_query or signoz_query_metrics.Early-stop gate: if no neighbor signal shows ≥ 25% deviation from baseline, classify as "isolated fire — the alert's own signal moved but nothing else did." This is unusual and worth surfacing. Skip Tier 3 and go to Step 5 with hypotheses focused on the alert's own query (likely causes: data source change, instrumentation change, downstream silent failure that only shows in this metric).
Run only if Tier 2 found correlated neighbor anomalies. Drill into specific failing operations.
Traces (if the alert is service-scoped and traces are available):
signoz_search_traces for the fire window with filter:
service.name = <scope> AND has_error = true. Cap at top 20.name and status_message. Surface the sample's top 3 with one
trace ID each; do not call a 20-row sample count full-window frequency.signoz_get_trace_details for span attributes. Pass the
search row's trace_id as traceId plus the same absolute fire-window
start and end; otherwise the 6h default misses older incidents.Logs for the fire window:
signoz_search_logs with filter:
<scope_filter> AND severity_text IN ('ERROR', 'FATAL'). Cap
at top 20 most recent.body pattern (or exception.type if present). Surface
the top 3 distinct messages with counts.Cross-reference: do the traces and logs point at the same downstream service, dependency, or code path? If so, that becomes the leading hypothesis.
See references/baseline-comparison.md for query templates that pair
fire-window and baseline-window calls cleanly.
Use this exact section order. Lead with a TL;DR — engineers under pressure scan the top first and stop reading once they have what they need. Compression plus proof: every claim cites the MCP query that produced it; no generic "check logs / verify connectivity" filler.
1. TL;DR — one or two sentences, no more. Leading hypothesis, overall confidence, blast radius, and the single most useful next action. Example:
"checkoutservice error rate hit 12.4% (threshold 5%) for 8m at 14:32 UTC — most likely cause is payments-api timing out (high confidence). Open trace
7af3a09b…to see the failing call."
If no hypothesis reaches medium confidence, the leading line is "No clear root cause found." rather than a low-confidence guess dressed up as the answer.
2. What fired
The alert (id, name), the fire window (absolute UTC + relative),
peak magnitude ("error rate hit 12.4% vs. 5% threshold — 148% over"),
fire duration, and the fire pattern (one-off / sustained /
flapping / recurring / marginal).
3. Investigation trail A scannable list of what was checked, with ✅ for confirmed signals and ❌ for ruled out, each followed by a one-line finding. The point is that the reader can see what work the AI did and what it found — this is where trust is earned. Example:
4. Likely causes (ranked, max 3) Each cause has three parts:
high requires ≥2 of: temporal precedence,
topology / dependency edge, shared service or entity, correlated
metric/log/trace evidence, recent deploy or config change.
medium is one tier's evidence with at least one of those
signals. low is a single signal moved with no corroboration —
in that case label it a "co-occurring signal," not a cause.If only Tier 1 ran (marginal fire / no neighbor anomalies), output
fewer hypotheses with low confidence and explicitly call out the
limitation.
5. Ruled out Short but explicit. List candidates the evidence eliminated and the one-line reason why. Skip the section if there's nothing meaningful to rule out — but if you considered something and dropped it, say so here so the user doesn't waste time re-checking it.
6. Suggested next steps Action items the user can take. Be concrete and use SigNoz-native handles so the user can act immediately:
7af3a09b… in the SigNoz UI").signoz-generating-queries — paste
the exact filter and time window.matchType, target, recoveryTarget) and the change to make
via signoz_update_alert.Do not pad with generic advice ("verify connectivity", "check dashboards") — that's noise during an active incident.
Mirroring as navigation chips. Mirror up to 3 of these "Suggested next steps" as host follow-up intents — the most actionable, alert-scoped ones. Keep the rest in the report prose so the user has the full picture. The chip surface is capped; the prose is not.
signoz-creating-alerts with anomaly_rule).signoz_update_alert.signoz-explaining-alerts. Cause analysis without an alert anchor
belongs to signoz-generating-queries.User: "Why did the checkout error rate alert fire?"
Agent:
Resolves alert: "High Error Rate — Checkout" (id 42).
signoz_get_alert_history → most recent fire 2h ago at 14:32 UTC,
sustained for 8m, single fire (not flapping).
Tier 1: re-runs error-rate formula over [14:02, 15:02]. Peak
error rate 12.4% (vs 5% threshold — 148% over). Pre-fire baseline
0.3%. Real fire, not marginal.
Tier 2: pulls neighbor signals for service.name = checkout:
payments error rate: 18% vs 0.2% baseline (+8900%).Tier 3: traces for service.name = checkout, has_error = true
in the fire window — top operation POST /checkout/submit, top
error message "context deadline exceeded calling payments-api".
30 traces, all hitting the same downstream URL. Logs show
matching "payments client timeout" lines, 142 occurrences.
Output:
TL;DR: checkoutservice error rate hit 12.4% (threshold 5%) for 8m at 14:32 UTC. Most likely cause: payments-api timing out (high confidence — converging trace + log + neighbor evidence). Open trace
7af3a09b…to see the failing call.
context deadline exceeded; 142 matching timeout logs.7af3a09b… in the SigNoz UI; if
payments has its own alert, run signoz-explaining-alerts on
it, otherwise signoz-generating-queries for payments error
rate over the last 4h.User: "Investigate the flapping CPU alert on prod-api-3"
Agent:
Resolves alert (id 88, host.name = prod-api-3).
History: 7 fires in last 1h, alternating fire/resolve every 8-12 minutes — flapping pattern.
Tier 1: peak 84% (threshold 80%, only 5% over). Each fire
lasted 2-4 minutes. Marginal — the value hovered near threshold.
matchType = at_least_once made each blip trigger.
Early-stop kicks in. Skip tiers 2/3.
Output:
TL;DR: alert 88 has been flapping on
prod-api-3— 7 fires in the last 1h, each 2–4m, all within 5% of the 80% threshold. No clear root cause; this is threshold tuning, not an incident. SwitchmatchTypetoon_averageor add hysteresis to stop the noise.
prod-api-3, fire pattern
flapping. 7 fires in the last 1h, each 2–4m. Peak 84% (5%
over the 80% threshold).matchType is too sensitive. Evidence:
every fire was within 5% of threshold; baseline already runs
at 70–75%. Not promoted to a "cause" — single signal, no
corroboration.matchType to on_average (smooths
transient spikes) OR raise threshold to 85% with hysteresis
(recoveryTarget: 75). Use signoz_update_alert to apply.User: "RCA for the log volume alert that fired last night"
Agent:
Resolves alert: "Error Log Volume Spike" (id 14, no service
filter — groupBy service.name).
History: fired at 03:12 UTC, sustained 22m, broke down by service
in the alert annotations: service.name = inventory was the
firing series.
Tier 1: re-runs log count for inventory in fire window. Peak 3,400 errors/min vs 1,000/min threshold (240% over). Pre-fire baseline 12/min. Real, large fire.
Tier 2: neighbor signals for service.name = inventory:
Tier 3: logs for inventory in fire window. Top message: "OOMKilled restarting" (1,200 occurrences). Top trace error: graceful-shutdown exceptions.
Output:
TL;DR: log volume alert 14 fired at 03:12 UTC for
service.name = inventory, sustained 22m at 240% over threshold. Most likely cause: inventory pods OOM-killed and restarted 4 times (high confidence). Check container memory limits for the inventory deployment.
inventory, sustained 22m, 240% over threshold.service.name = inventory), and a single coherent
pattern (OOM → restart → graceful-shutdown noise).references/neighbor-signals.md — lookup table mapping resource type
(service / host / k8s) to the neighbor signals to pull in Tier 2.references/baseline-comparison.md — query templates that pair
fire-window and baseline-window calls cleanly, including how to
format signoz_execute_builder_query for both.signoz-explaining-alerts skill — to decode the rule before
investigating, if the user is unfamiliar with what the alert
monitors.signoz-generating-queries skill — for ad-hoc follow-up queries on the same
resource scope.