﻿---
title: Diagnose rule behavior with the rule query inspector
description: Use the rule query inspector to view the Elasticsearch request behind a rule and diagnose why an alert did or didn't fire.
url: https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/6746/explore-analyze/alerting/alerts/inspect-rule-queries
products:
  - Kibana
applies_to:
  - Elastic Cloud Serverless: Generally available
  - Elastic Stack: Planned
---

# Diagnose rule behavior with the rule query inspector
The rule query inspector lets you view the Elasticsearch request that a rule sends when it evaluates your data. Use it to understand the query structure, confirm the rule is targeting the right data, and diagnose why an alert did or didn't fire.
<note applies-to="Elastic Cloud Serverless: Generally available, Elastic Stack: Planned">
  Currently, the rule query inspector is only available for **custom threshold rules**.
</note>


## Access the inspector

The inspector is available from two places, each showing a different query:
<definitions>
  <definition term="From the rule details page (current rule parameters)">
    Open **Stack Management** > **Rules**, find your rule, and click its name to open the rule details page. Click **Rule query inspector**. The inspector builds the query from the rule's _current_ parameters. Use this view to verify that the rule is configured correctly and would match the data you expect.
  </definition>
  <definition term="From an alert details page (historical parameters)">
    Go to the **Alerts** page, then open an individual alert. Click **Rule query inspector**. The inspector uses the rule parameters _as they existed when that specific alert fired_, including the exact evaluation time range. Use this view to understand why a particular alert was or wasn't triggered.
  </definition>
</definitions>

The key difference: the rule details page reflects the rule as it is _now_, while the alert details page reflects the rule as it was _then_. If you've edited the rule since an alert fired, the two inspectors will show different queries.

## Anatomy of the query

The following sections describe the query structure for **custom threshold rules**. As support for additional rule types is added, this reference will expand.
The inspector displays the full Elasticsearch request. Each part of the query maps to a setting in your rule configuration.

### Index and time range

The top-level index and `range` filter reflect your rule's data source and time window:
```json
{
  "index": ["<your-data-view-index-pattern>"],
  "body": {
    "query": {
      "bool": {
        "filter": [
          {
            "range": {
              "@timestamp": {           
                "gte": "...",           
                "lte": "..."            
              }
            }
          }
        ]
      }
    }
  }
}
```

If the time range looks unexpected from an alert details page, this confirms the exact window Elasticsearch searched when the alert fired. This can help explain alerts that seem outdated or cover an unexpected period.

### Query filter

If you set a **query filter** on the rule, it appears as an additional clause in the `bool` filter:
```json
{
  "query": {
    "bool": {
      "filter": [
        { "range": { "@timestamp": { ... } } },
        { "query_string": { "query": "host.name: host-1" } }   
      ]
    }
  }
}
```

If the filter is missing or different from what you set, double-check the rule configuration.

### Aggregations

Each criterion you defined in the rule becomes an aggregation in the query. A rule with two criteria (for example, Aggregation A and Aggregation B) produces two sub-aggregations:
```json
{
  "aggs": {
    "A": {                                     
      "avg": { "field": "system.cpu.user.pct" }
    },
    "B": {
      "avg": { "field": "system.cpu.system.pct" }
    }
  }
}
```


| Rule criterion                 | Aggregation in query                        |
|--------------------------------|---------------------------------------------|
| **Average** of a field         | `avg`                                       |
| **Max** of a field             | `max`                                       |
| **Min** of a field             | `min`                                       |
| **Sum** of a field             | `sum`                                       |
| **Count** (all docs)           | `value_count` or `filter` + `value_count`   |
| **Cardinality** of a field     | `cardinality`                               |
| **95th percentile** of a field | `percentiles` with `{ "percents": [95] }`   |
| **Rate** of a field            | Two `max` aggregations plus a bucket script |

If you set a **KQL filter** on a criterion (<applies-to>Elastic Stack: Generally available since 9.4</applies-to>), it appears as a `filter` aggregation wrapping the metric aggregation.

### Group-by fields

If your rule uses **Group alerts by**, the aggregations are wrapped in a `composite` aggregation that partitions results by those fields:
```json
{
  "aggs": {
    "groupBy": {
      "composite": {
        "sources": [
          { "host.name": { "terms": { "field": "host.name" } } }   
        ],
        "size": 10000
      },
      "aggs": {
        "A": { "avg": { "field": "system.cpu.user.pct" } }
      }
    }
  }
}
```

Without group-by, the aggregations run over all matched documents and return a single value.

## Reading the response

The inspector also shows the Elasticsearch response alongside the request. Match each aggregation bucket back to your rule configuration to understand what value was computed.

### No group-by: single-value response

When there are no group-by fields, the response contains a single set of aggregation values under `aggregations`:
```json
{
  "aggregations": {
    "A": { "value": 0.82 },      
    "B": { "value": 0.15 }
  }
}
```

If the response value is below the threshold and no alert fired, this confirms the rule evaluated correctly. If you _expected_ an alert and the value is below the threshold, review your aggregations and KQL filters.

### With group-by: bucketed response

When group-by fields are used, the response returns one bucket per group under `aggregations.groupBy.buckets`:
```json
{
  "aggregations": {
    "groupBy": {
      "buckets": [
        {
          "key": { "host.name": "host-1" },
          "doc_count": 342,
          "A": { "value": 0.97 }       
        },
        {
          "key": { "host.name": "host-2" },
          "doc_count": 58,
          "A": { "value": 0.42 }       
        }
      ]
    }
  }
}
```

If a group you expected to appear is missing from the buckets, it had no matching documents during the evaluation window. This can happen when `doc_count` is 0 or when the query filter excluded all documents for that group.

### What a "no data" response looks like

If Elasticsearch returned no documents, the aggregation values will be `null` or the buckets array will be empty:
```json
{
  "aggregations": {
    "A": { "value": null }
  }
}
```

A `null` value means no data matched the query during the evaluation window. If you have **no data** alerts configured, this is the state that triggers them. Check the time range and query filter to confirm no documents were genuinely present, or investigate whether an index or data view configuration issue is preventing data from being found.

## Common troubleshooting scenarios

<dropdown title="Alert fired but I don't know why">
  Open the inspector from the alert details page. Review the time range to confirm it matches the evaluation period. Find the aggregation bucket for your group and check the value against the threshold. If the value exceeds the threshold, the alert fired correctly.
</dropdown>

<dropdown title="Alert didn't fire when I expected it to">
  Open the inspector from the rule details page and confirm the query targets the right index pattern and time range. Check the query filter for unintended restrictions. If the aggregation values in the response are below the threshold, the rule evaluated correctly but your data didn't breach the threshold during that window.
</dropdown>

<dropdown title="Rule looks correct now but the alert used different parameters">
  If you've modified the rule since the alert fired, open the inspector from the _alert details page_ rather than the rule details page. The alert inspector uses the parameters that were active at the time the alert fired, so the query will reflect the older configuration.
</dropdown>

<dropdown title="Empty or null aggregation values">
  The query matched no documents. Check whether the index pattern in the data view is correct, whether your time range is appropriate, and whether any query filter is too restrictive. Also verify that the data stream or index has data in the expected time period by running the same query in [Discover](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/6746/explore-analyze/discover) or [Dev Tools](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/6746/explore-analyze/query-filter/tools/console).
</dropdown>

<dropdown title="Unexpected group missing from results">
  If a group you expected (such as a specific host) doesn't appear in the buckets, no documents for that group matched the query during the evaluation window. This can happen when the group was inactive, when a filter excluded its documents, or when the field used for grouping has a different value in the actual documents than you expected.
</dropdown>