Home › Observability › Grafana Panel No Data: Fix Valid Queries Fast
Beginner 5 min · September 23, 2026

Grafana Panel No Data: Fix Valid Queries Fast

Grafana panel empty but the query works in Prometheus? Fix time range, datasource UID, variables, and min interval in order..

N
Naren Founder & Principal Engineer

20+ years shipping production backend systems. Drawn from code that ran under real load.

Follow
✓ Production
production tested
September 27, 2026
last updated
2,085
articles · all by Naren
Before you start⏱ 8 min
  • ✓A Grafana instance with a Prometheus datasource saved
  • ✓One panel showing No Data plus the raw query text
  • ✓Access to the Prometheus expression browser
 ● Production Incident 🔎 Debug Guide
⚡Quick Answer
  • Widen the range past 4x the scrape interval since short ranges on slow jobs hold too few samples to draw
  • Check the datasource UID in Query Inspector because clones silently point at the old environment's backend
  • Hardcode template variables one by one since a stale pod name filters every series out quietly
  • Set min interval at or above the scrape interval so evaluation steps stop flickering between data and gaps
✦ Definition~90s read
What is Grafana Panel Shows No Data Despite Valid Query?

A Grafana panel renders a query through four layers before pixels appear. The time range selects which samples to consider; the datasource UID routes the request to a backend; template variables interpolate filter values into the query text; the interval stepper decides evaluation granularity.

★
Think of Grafana as a waiter carrying your exact order to the kitchen.

The backend result passes back through the same layers for display. No Data means the final result was an empty set — every layer agreed to return nothing.

Each layer fails silently in its own way. Short ranges on slow-scrape jobs hold too few samples for range functions. Stale UIDs from clones and migrations route queries to deleted or wrong backends. Template variables hold extinct label values after deploys and filter everything out. Min intervals below the scrape interval slice the range into empty slots that render as flickering gaps.

Diagnosis therefore proceeds layer by layer, cheapest first: widen the range, verify the UID in Query Inspector, hardcode variables to test them, and match min interval to the scrape interval. The rendered query pasted into Prometheus splits the fault decisively — empty in both systems means the query or timing is wrong, while data in Prometheus with emptiness in Grafana convicts the panel layers.

Plain-English First

Think of Grafana as a waiter carrying your exact order to the kitchen. The order (query) is perfect, but the waiter checks four things on the way: is the dining window open (time range), which kitchen branch (datasource), any substitutions from the specials board (variables), and how finely to slice the portions (interval). Any one of them can quietly cancel your order without telling you. Fix the waiter's route, not the recipe.

The query runs perfectly in Prometheus. Paste it into Grafana and the panel says No Data. You check the datasource — green, saved, healthy. You retype the query, refresh, clone the panel. Still nothing, and the wallboard meeting starts in twenty minutes.

Grafana panels add four layers between your query and the screen: time range, datasource routing, template variables, and interval stepping. Each layer can silently empty a valid query, and none of them reports an error when it does. No Data is Grafana shrugging — the query returned nothing after all four layers had their say.

This guide checks the layers in order, cheapest first. You'll widen ranges to fit scrape intervals, verify datasource UIDs with Query Inspector, unstick template variables, and set min intervals that match collection. Most empty panels fill in under ten minutes once you stop blaming the query. Bring one broken panel and its raw query text: you will fix it live, name the exact layer that lied, and lock it for good.

Time Range Too Short for Your Scrape Interval

Time range is the top empty-panel cause because defaults lie. A 5-minute default range on a 60s-scrape job holds five samples, and range functions needing two points per window plus step misalignment turn that into flickering gaps. Engineers see No Data while colleagues with 6-hour ranges see healthy lines from the identical query.

The fix is arithmetic: range at 4x the scrape interval or wider, min step at or above the interval. Sixty-second jobs get 15-minute minimum ranges with 60s steps; 15s jobs are comfortable at 30 minutes with auto steps. Wallboards deserve 6-hour defaults so slow jobs always have room to draw.

Lock it per dashboard. Document the floor in the dashboard description and set panel defaults so the next viewer can't shrink into gaps. Range bugs recur whenever someone clones a panel and narrows the view to investigate — the floor prevents the investigation from manufacturing its own mystery.

The time picker hides more traps: absolute ranges pinned during an incident keep showing the incident long after recovery, and auto-refresh intervals shorter than the scrape paint partial data as fresh. Prefer relative ranges (last 6h) for routine dashboards and reserve absolute ranges for postmortems. The $__range variable lets panels adapt their computations to the visible window instead of hardcoding lookbacks. When a wallboard disagrees with your laptop, compare picker settings first — different ranges explain most ghost disagreements. Time is a filter, and filters silently empty queries.

PROMQL
1
2
3
4
5
6
# Rendered correctly: range covers many scrape intervals
# Panel: Range last 6h, Min interval 60s (job scrapes every 60s)
rate(http_requests_total[5m])

# Gappy: same query, 5m range with 10s min interval on a 60s job
# Fix: widen range, raise min interval — don't touch the query
📊 Production Insight
An executive wallboard on 60s jobs showed No Data every Monday — someone kept resetting it to a 5m default. A 6h default with 60s min interval ended a year of Monday mysteries.
🎯 Key Takeaway
Range at 4x the interval with steps above it; short ranges on slow jobs flicker to empty.

Datasource UID Pointing at Nothing

Grafana routes panels by datasource UID, an opaque string that survives renames but changes on recreation. Cloned dashboards, migrated backends, and hand-edited JSON all carry stale UIDs that point at deleted or wrong-environment datasources. The panel queries nothing and reports No Data without complaint.

Query Inspector exposes the routing in seconds. It shows the exact UID hit, the rendered request, and the raw response — a 404 or wrong-cluster payload names the culprit immediately. Compare against a working panel's UID and the mismatch is usually obvious.

Provisioning ends the class. Datasource provisioning files pin stable UIDs per environment, and dashboard JSON references those UIDs instead of whatever the UI generated. Migrations then update the provisioned backend behind a stable UID, and no panel ever orphans again.

Provisioning files make UIDs boring in the best way. Declare each datasource once with a fixed uid, and every dashboard references that string instead of whatever the UI generated. Across environments, keep the same logical names with per-environment URLs: prod-prometheus points at prod in prod and staging in staging, so one dashboard JSON deploys everywhere. Export and import round-trips preserve UIDs, but hand-merging JSON in editors often duplicates or drops them — diff UID fields after every merge. Treat UIDs like DNS: stable, boring, load-bearing.

BASH
1
2
3
4
5
6
7
8
9
10
11
# Find stale UID references across exported dashboards
# Export JSON, then:
# grep -o '"uid": *"[^"]*"' dashboard.json | sort | uniq -c

# datasource provisioning with a stable UID (provisioning/datasources.yml)
apiVersion: 1
datasources:
  - name: Prometheus-Prod
    uid: prod-prometheus
    type: prometheus
    url: http://prometheus:9090
📊 Production Insight
A migration created a fresh UID and orphaned 200 panels in a minute. A JSON find-replace repointed them in 20 minutes; provisioned UIDs would have made the incident impossible.
🎯 Key Takeaway
Verify UID in Query Inspector; provision stable UIDs so migrations never orphan panels.

Template Variables Filtering Everything Out

Template variables interpolate into queries before execution, and a stale value filters everything. After Kubernetes deploys, pod-name variables hold last week's pods; after renames, instance variables hold dead hostnames. The query is valid, the value is extinct, and the result is empty.

Prove it by hardcoding. Replace $pod with a live pod name and rerun — data means the variable lied. Do this per variable, most-stale first, and you'll find the culprit in under a minute without reading variable definitions.

Harden the variables afterward. Set refresh on dashboard load and time-range change, default to All instead of one value, and prefer stable label values like service or deployment over pod names. Variables built on volatile labels are incident generators with a refresh button.

Variable types shape failure modes. Query variables pull values from the datasource and go stale when labels churn; custom variables hold hand-written lists that rot when services rename; constant variables hide filters nobody remembers. Chained variables (region filters zone filters pod) multiply staleness: one stale parent empties every child. Prefer query variables with refresh-on-load for volatile labels, and constants only for truly fixed dimensions like environment tiers. Audit the variable list quarterly — dashboards accumulate variables like closets accumulate boxes.

📊 Production Insight
Forty deploy dashboards emptied after every rollout because pod variables held dead names. Switching variables to deployment labels plus refresh-on-load ended the ritual of manually reselecting pods.
🎯 Key Takeaway
Hardcode values to convict the stale variable; refresh on load and default to All.

Min Interval Finer Than Your Data

Min interval controls step granularity: how finely Grafana slices the range for evaluation. Steps far below the scrape interval create empty slots — a 10s step on a 60s job means five of six slots hold no sample. The line flickers between points and gaps that look like outages but are pure arithmetic.

Match the step to collection. Set min interval to the scrape interval explicitly, or leave it on auto with a sane floor. Either guarantees every evaluation slot contains a sample, and the flicker vanishes without touching queries or backends.

Beware copy-paste intervals. A 10s min interval tuned for a fast job travels with cloned panels onto slow-job dashboards and manufactures gaps there. Review min interval on every clone like you'd review the query — it's part of the query's correctness, not decoration.

Two interval variables solve different problems. $__interval divides the visible range into Grafana's max data points, adapting to zoom; $__rate_interval adds scrape-interval awareness so rate() windows never drop below four scrapes. Prefer $__rate_interval inside PromQL range selectors and plain $__interval for display steps. Instant queries ignore intervals entirely, which makes single-stat panels flicker on slow jobs — switch them to range queries with explicit steps. The interval system rewards dashboards that declare collection speed once per panel and reuse it everywhere.

JSON
1
2
3
4
5
6
7
8
# Panel JSON: min interval matched to a 60s scrape job
{
  "fieldConfig": { "defaults": {} },
  "targets": [
    { "expr": "rate(http_requests_total[5m])",
      "interval": "60s" }
  ]
}
📊 Production Insight
A cloned API dashboard flickered for weeks on 60s jobs with a 10s min interval inherited from a 15s job. One field change to 60s fixed every panel at once — the queries were never wrong.
🎯 Key Takeaway
Min interval at or above the scrape interval; copied 10s steps manufacture gaps on slow jobs.

Query Inspector: Splitting Grafana vs PromQL Faults

Query Inspector is the single tool that splits every empty-panel case. It shows the fully rendered query with variables interpolated, the datasource UID and request sent, the raw response received, and per-stage timings. Empty response with a sane rendered query means filters or timing; error response means routing or backend.

Build the split habit. Copy the rendered query into the Prometheus expression browser at the same range: empty in both means PromQL or staleness — take the Prometheus checklist. Data in Prometheus but empty in Grafana means variables, interval, or UID — stay in Grafana. One paste eliminates half the system from suspicion.

Teach the team the two-minute version. Range, inspector, hardcode variables, browser paste — in that order. Panels that once took hours of guesswork resolve before coffee cools, because evidence replaces theories at each step.

Each Inspector tab answers one question. Query shows the interpolated PromQL after variables expanded — copy it to Prometheus to split the fault. Request shows headers, UID, and timestamps for routing bugs. Response shows raw payloads where empty data arrays convict filters and error objects convict backends. Stats shows duration per stage, separating slow backends from slow browsers. Screenshot all four tabs into incident tickets; the next debugger starts from evidence instead of folklore. Inspector fluency is the difference between guessing and knowing.

BASH
1
2
# Replicate the panel query against Prometheus directly (what Inspector shows)
curl -s "http://prometheus:9090/api/v1/query_range?query=rate(http_requests_total[5m])&start=1699999200&end=1700002800&step=60s" | head -c 500
📊 Production Insight
A team made Inspector-paste mandatory in their runbook and cut empty-panel resolution from 90 minutes to 9. Most cases never left the first two steps.
🎯 Key Takeaway
Rendered query plus raw response in Inspector, pasted into Prometheus, halves the suspect system instantly.

Locking the Fix: Versions, Docs, and CI Checks

Lock in the fix so the next on-call inherits a working dashboard, not a mystery. Save a version with an annotation naming the cause, document range and interval floors in the description, and set variable refresh defaults that survive deploys. A fixed panel without documentation breaks again on the next clone.

Add lightweight CI for provisioned dashboards: fail on unknown datasource UIDs, volatile default variable values, and min intervals below the owning job's scrape interval. These checks are cheap JSON assertions that catch the entire bug class before merge.

Finally, keep one known-good reference dashboard per datasource. When panels empty, diffing against the reference separates systemic faults (reference also empty — backend or time issue) from local ones (reference fine — panel settings). That single comparison halves diagnosis before any deeper digging starts.

Dashboard version history is an underused safety net: every save snapshots the JSON, and restore takes one click when an edit empties panels. Library panels share one definition across dashboards, so a fix propagates everywhere — but a bad edit does too, so test library changes on a scratch dashboard first. Viewer permissions confuse debugging when teams share links: a viewer without datasource permission sees empty panels that admins cannot reproduce. Check the share settings and the viewer's role before deep debugging. Most mysteries are access control wearing a No Data costume.

💡Prove the Query Before Blaming the Panel
Always paste the panel's rendered query into Prometheus before changing Grafana settings. If it's empty there too, every minute spent on datasources and variables is wasted — the bug is PromQL, names, or timing, and Grafana was just the messenger.
📊 Production Insight
After adding UID and interval CI checks, one org went a full year without an empty-panel page. The reference dashboard caught the single backend outage in minutes instead of hours.
🎯 Key Takeaway
Annotate, document floors, CI-check UIDs and intervals, keep a reference dashboard per backend.
● Production incidentPOST-MORTEMseverity: high

The Migration That Orphaned 200 Wallboards in a Minute

Symptom
All 200 wallboard panels flipped to No Data within a minute of a Prometheus migration at 9 AM. Prometheus targets were green, test-and-save passed on the new datasource, and the NOC declared a monitoring outage.
Assumption
The team assumed the Prometheus migration had dropped history because every wallboard panel emptied at once. They rolled back the migration, which caused a second outage, before anyone opened Query Inspector.
Root cause
The migration replaced the Prometheus datasource, generating a new random UID. All 200 wallboard panels referenced the old UID, so every query routed nowhere and returned No Data. Prometheus itself was healthy throughout — only Grafana's routing pointers were stale.
Fix
One engineer found every panel still pointed at the old datasource UID — the migration created a new datasource instead of updating the old one. They repointed panels via a JSON find-replace, provisioned UIDs per environment, and added a CI check that fails dashboards referencing unknown UIDs. Wallboards recovered in 20 minutes.
Key lesson
  • Datasource migrations must preserve UIDs or repoint every panel — new UIDs silently orphan all references.
  • Query Inspector before rollbacks: ten seconds there would have prevented a second outage.
  • Provisioned dashboards with pinned UIDs make this class of incident impossible.
Production debug guideFive steps across ranges, routing, variables, queries, and lockdown.5 entries
Symptom · 01
Valid query shows No Data in the panel
→
Fix
Widen the panel range to 6 hours and set min interval to the job's scrape interval (60s for slow jobs). If the line appears, the query was always fine — the range was shorter than two scrape intervals or the step created empty slots. Lock in range floors per dashboard so it can't recur.
Symptom · 02
Range fix didn't help; suspect routing
→
Fix
Open Query Inspector, copy the rendered request, and check which datasource UID it hit plus the raw response. A 404 or wrong-cluster data means the panel points at a stale UID (common on clones). Re-point the panel to the correct datasource and prefer provisioned UIDs that stay stable across environments.
Symptom · 03
Datasource is correct but panel still empty
→
Fix
Temporarily replace each $variable in the query with a hardcoded value you know exists, starting with the one most likely stale (pod, instance). When data returns, that variable was filtering everything out. Set it to refresh on dashboard load, fix its default, and prefer stable label values like service over pod names.
Symptom · 04
Need to prove whether Grafana or PromQL is at fault
→
Fix
Paste the inspector's rendered query into the Prometheus expression browser with a matching 6h range. Empty there too means the bug is PromQL or timing, not Grafana — take the Prometheus checklist (names, matchers, staleness) instead. Non-empty in Prometheus but empty in Grafana points back at variables or interval.
Symptom · 05
Panel fixed; keep it fixed for the next on-call
→
Fix
Set min interval explicitly to the scrape interval, confirm the variable refresh triggers (dashboard load plus time change), and save a known-good dashboard version with an annotation of what fixed it. Document the per-job range and interval floors in the dashboard description for the next on-call.
Empty-Panel Causes Compared
Root CauseHow to ConfirmFixPrevention
Time range shorter than scrape intervalWidening range restores the line instantlyRange >= 4x interval; min step >= intervalDefault dashboards to 6h; document per-job floors
Wrong datasource UIDQuery Inspector shows 404 or wrong cluster dataRe-point panel to correct UID; provision UIDsProvision datasources; review UID on clone
Stale template variable valueHardcoding the value returns dataRefresh values on load; fix defaultsStable label values; All-option defaults
Min interval below scrape intervalSetting min interval to scrape interval fixes flickerMin interval >= scrape intervalLeave auto unless you know the interval
⚙ Quick Reference
4 commands from this guide
FileCommand / CodePurpose
rate(http_requests_total[5m])Time Range Too Short for Your Scrape Interval
apiVersion: 1Datasource UID Pointing at Nothing
{Min Interval Finer Than Your Data
curl -s "http://prometheus:9090/api/v1/query_range?query=rate(http_requests_tota...Query Inspector

Key takeaways

1
No Data means empty result, not broken panel
check the four layers in order.
2
Widen ranges past 4x the scrape interval before touching anything else.
3
Verify datasource UID in Query Inspector, especially on cloned dashboards.
4
Hardcode variable values to prove variables innocent or guilty in seconds.
5
Keep min interval at or above the scrape interval to end flicker.
6
Prove the raw query in Prometheus first so Grafana debugging starts from truth.

Common mistakes to avoid

5 patterns
×

Leaving a 5-minute default range on a slow-scrape dashboard

Symptom
Executives see No Data on wallboards while engineers with wider ranges see healthy lines from the same query.
Fix
Set the panel range to cover 4x the scrape interval and min interval at or above it. For 60s jobs use 15m ranges with 60s min interval as a floor.
×

Cloning panels across environments without fixing the datasource

Symptom
Staging dashboards work and production clones show No Data because the UID still points at staging.
Fix
Point every panel at the datasource by UID and re-check after any datasource edit. Use provisioning files so UIDs stay stable across environments.
×

Ignoring template variables that silently select nothing

Symptom
The query is perfect but the variable feeds it a pod name from last week's deploy, filtering everything out.
Fix
Preview every variable's value list and set a sane default plus an All option. Never leave a multi-value variable defaulting to a deleted label value.
×

Setting min interval far below the scrape interval

Symptom
Graphs flicker between data and gaps on every refresh while the raw query in Prometheus returns complete lines.
Fix
Keep min interval at auto or the scrape interval. Custom 10s min intervals on 60s jobs manufacture gaps that look like outages.
×

Debugging Grafana settings for a broken PromQL query

Symptom
Hours in datasource and variable settings while a metric typo sits in the query box the whole time.
Fix
Test the panel query in the Prometheus expression browser first. If it's empty there, fix PromQL before touching Grafana settings — the panel was innocent.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
A panel shows No Data. What's your check order?
Q02JUNIOR
Why does Grafana reference datasources by UID?
Q03SENIOR
Why does a 10s min interval gap on a 60s job?
Q04SENIOR
What breaks panels after every Kubernetes deploy?
Q05SENIOR
How do you use Query Inspector to split the fault?
Q01 of 05JUNIOR

A panel shows No Data. What's your check order?

ANSWER
Check time range first (widen to 6h), then datasource UID (Query Inspector), then template variables (hardcode values), then the raw query in Prometheus. Order matters: each step is cheaper than the next and eliminates a whole layer.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Is No Data the same as a query error?
02
Can panels be empty with a healthy datasource?
03
What min interval should a Prometheus panel use?
04
How do I stop variables going stale after deploys?
05
Can I diff a working dashboard against a broken one?
06
How do I prove the panel query itself is broken?
N
Naren Founder & Principal Engineer

20+ years shipping production backend systems. Drawn from code that ran under real load.

Follow
✓ Verified
production tested
September 27, 2026
last updated
2,085
articles · all by Naren
🔥

That's Grafana. Mark it forged?

5 min read · try the examples if you haven't

←
Previous
Prometheus rate() vs increase() — Wrong Alert Thresholds
1 / 2 · Grafana
Next
Grafana Datasource Proxy Error: Connection Refused
→