Skip to main content

A detection rule never fires

Six reasons a detection stays quiet, in the order to check them: shadow status, an unpopulated field, a missing data source, a suppression, a cooldown, and ingest.

Written for whoever runs IT9 min readUpdated

You expected a detection to alert and it has not. This article is an ordered checklist. The order matters, because these are roughly the order the platform itself applies them, and because the first three causes are far more common than the last three. Work down it rather than starting with the most interesting explanation.

One framing point first. A match is not an alert. A detection passes four stages: whether it should be evaluated at all, whether the event matches its conditions, whether the windowed aggregation has crossed its threshold, and whether a rule in this state is allowed to create work for a person. A rule can match your event and still, correctly, produce nothing.

1. Is the rule in shadow status?

A rule whose status is testing is evaluated in full and does not create an alert. Its finding is real, it is recorded, and it contributes to the risk score for the entity involved. It simply stops one stage short of your queue — that is the point of shipping a rule in that state rather than switching it off.

The statuses a rule can hold are draft, testing, active, disabled and deprecated. Only active alerts. The coverage view counts shadow-mode rules separately from alerting rules for the same reason: a coverage view that counts shadow-mode rules as coverage is a coverage view that lies.

2. Does it read a field nothing populates?

Every rule declares the fields it needs, and every field is registered in a contract recording whether anything actually produces it. Fields nothing populates are marked pending, and the rule view lists them as pending_fields; the coverage view aggregates the same thing as blocked_on_telemetry.

This is not a mapping bug. The fields in question are computed inside other parts of the platform — privileged access, identity, the AI control plane — and none of those writes an event into the log pipeline, so there is nothing for the projection to derive from. A rule needing how many days this service account has been dormant cannot fire because nothing has told the SIEM that number. One is blocked externally: DNS record type is reported by no telemetry we ingest, and deriving it from the answer rather than the question would be the opposite of what a DNS-tunnelling rule needs.

If a rule you care about lists pending fields, no configuration will make it fire. This is also why validation refuses to mark a rule with a pending required field as active: a rule that cannot match anything must not present itself as coverage.

3. Is the source producing the data it needs?

Every rule names its data sources, and that field is mandatory precisely so the console can tell you what telemetry a rule needs. Filter the rule list by data source and compare it with what you collect. Connecting Microsoft 365 does not make a Google Workspace rule fire.

Two subtler versions. A source can be connected and producing the wrong shape: with the wrong parser on its token, events arrive as generic records and the fields a rule addresses are absent. And some parser kinds are accepted but not implemented — a token issued for CEF or ECS falls through to the generic JSON parser rather than being refused, so events land unparsed and every field-based rule misses them silently. Check the parser kind on that collector's token.

4. Is a suppression matching?

A suppression drops events from a rule's evaluation when a named field holds one of a list of values — the mechanism behind stop alerting on the backup service account. It is applied at the first stage, before the rule's own conditions are tested, so a matching suppression means the event is never considered.

Suppressions are the commonest self-inflicted cause of a quiet rule, because they are added during tuning and rarely revisited. Open the rule and read them; the rule view shows the tuning knobs and the false-positive guidance side by side, because the guidance is what tells you which knob was probably turned. Suppression fields are validated against the field contract, so you cannot suppress on a field that does not exist:

Suppression validation refusals
suppression <n>: field is required
suppression <n>: field "<name>" is not in the event field contract
suppression <n>: at least one value is required

5. Is a cooldown or a threshold swallowing it?

This is where a rule matches and produces nothing, which is the hardest case to reason about from outside. Five settings govern it.

The aggregation settings that keep a matching rule quiet, and how each does it.
SettingHow it hides a match
Time window — 5 minutes when unset or unparseableMatches spread wider than the window never accumulate. Ten failed sign-ins over an hour do not satisfy a five-minute threshold.
Threshold — events needed per group key; zero is treated as oneBelow the threshold nothing fires. The usual reason a single test event produces no alert.
Distinct field — counts distinct values, not eventsAn event not carrying the field is dropped from the window entirely, since counting it would inflate the distinct count with an empty value. A partially mapped source makes such a rule unreachable.
Minimum events — a floor checked after the thresholdYou can satisfy the breadth and fail the depth: this many values, drawn from at least this many events.
Cooldown — a quiet period per rule and group keyLater crossings inside it are marked suppressed rather than alerting. Without it, crossing a threshold would alert on every subsequent match.

The cooldown case is visible rather than silent, and that is what to look for. A suppressed decision is surfaced rather than dropped, precisely so the console can show further occurrences suppressed against the existing alert. If you expected a second alert and see a count rising on the first, the cooldown is doing its job.

One capacity note that affects accuracy rather than firing: a group's window holds a bounded number of events, and beyond that the oldest are discarded and the result is flagged truncated, so the count is a floor rather than exact. A flood degrades a distinct count's precision, not the process, and does not stop a rule firing.

6. Is the event reaching the engine at all?

Check this last in the list and first in your suspicions if your logs arrive over a connector, because it is the largest single reason a detection library produces no alerts today.

The test is simple: search for the event. If it is searchable, ingestion works and the silence is either this gap or one of the five causes above. If it is not searchable, the problem is upstream of detection entirely — go to A log source is ingesting nothing and start with the token.

When we refuse your change to a rule

If you are trying to fix a quiet rule by tuning it, these are the refusals you may hit. All are returned as {"error": "<message>"}.

Tuning validation refusals
rule_ref is required
status "<value>" cannot be set by a tenant override
unknown severity "<value>"
risk_score <n> is outside the 0-100 band
threshold must be positive
min_events must be positive
timewindow_sec must be positive
cooldown_sec may not be negative
a reason is required when disabling a detection rule
organization id is required
organization id and rule ref are required
invalid request body: <decoder detail>
rule not found
rule tuning requires a configured database

Two deserve a sentence. status "deprecated" cannot be set by a tenant override is the one people meet: you may set draft, testing, active or disabled. A rule that ships deprecated stays off whatever you ask, because the definition is no longer maintained and honouring an enable would ship you unmaintained detection logic. And a reason is required when disabling a detection rule is not bureaucracy — who turned off the ransomware rule and why is asked after every incident, and an empty audit row cannot answer it. Note too that lowering a rule's severity lowers its risk contribution, unless you set the risk score in the same request.

A rule can also be written so that it is guaranteed silent, and validation catches most of that before the library loads. An unrecognised operator is refused with <selection>: unknown operator "<op>", because a rule naming an operator the evaluator does not implement returns false forever. Patterns are compiled at validation time for the same reason: the expression engine rejects lookahead, and a pattern that fails to compile does not error when matching — it never matches. That is how one shipped rule was dead on arrival before this gate existed.

What to do

  1. Read the rule's own row before changing anything

    Status, pending fields, data sources, suppressions, threshold, minimum events, window and cooldown are all on it. Five of the six causes here are answerable from that one page.

  2. Search for the event you expected to trigger it

    Not searchable means an ingestion problem. Searchable but arrived over a connector means it did not reach the engine, and no tuning will help.

  3. Compare your test against the window and the threshold

    One synthetic event will not satisfy a rule needing ten in five minutes, nor a distinct-count rule if your test reuses one value. Generate the shape the rule describes.

  4. Look for a cooldown before assuming nothing fired

    Check whether an existing alert is accumulating suppressed occurrences. If it is, the rule fired once and is deliberately quiet.

  5. If the rule is shadow or blocked on telemetry, stop tuning it

    Neither is fixable from the console. Spend the effort on a rule whose data you collect. Tuning a detection rule covers what is worth adjusting.

Was this article wrong?

If a procedure here does not match what you see, or a limit we described has changed, tell us and we will fix the page. Email us about this article, or see how to get help if you need an answer rather than a correction.

Everything in troubleshooting