Grafana Dashboard Import Issue: JSON Model Conflicts and Datasource Mapping
Troubleshooting Grafana dashboard imports that silently fail or show blank panels due to datasource UID mismatches and schema version conflicts.
Introduction
Grafana is my go-to for visualizing everything from application metrics to trading system performance. I have been running it for about two years now, and the import feature is something I use constantly. But recently, I ran into a frustrating problem. I exported a dashboard from one Grafana instance and tried to import it into another. The import appeared to succeed β no error messages, no warnings β but every single panel was blank. No data, no error overlays, just empty canvases.
After spending an entire afternoon digging into this, I discovered it was a combination of two separate issues: datasource UID mismatches and a schema version gap between the two Grafana installations. Here is a detailed breakdown of what happened and how I resolved it.
Environment
My setup involves two Grafana instances:
- Source instance: Grafana 10.2.3 running on Docker, connected to a Prometheus datasource with UID
PBFA97CFB590B2093 - Target instance: Grafana 9.5.15 running on a separate VPS, with its own Prometheus datasource UID
prometheus-prod - Operating system: Ubuntu 22.04 on both machines
- Data source type: Prometheus (remote_write endpoints)
The source instance is my production monitoring stack. The target is a staging environment I use to test new dashboards before rolling them into production.
Problem
When I exported the dashboard JSON from the source instance and imported it into the target, the following symptoms appeared:
- All panels rendered as empty β no data points, no "no data" message, nothing
- The datasource dropdown in each panel showed "Broken" in red text
- The variables (template variables) at the top of the dashboard were populated, but selecting different values had no effect on the panels
- The Grafana server logs showed no errors related to the import
I initially thought the Prometheus endpoint was unreachable from the target instance, but manually querying the datasource via Grafana's built-in explore tool worked perfectly. This ruled out network connectivity issues.
Analysis
I started by comparing the exported JSON with the datasource configuration on the target instance. The first clue was in the panel datasource references. In Grafana 10.x, datasource references use a UID-based system exclusively:
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
}
}On the target instance, the Prometheus datasource had a completely different UID: prometheus-prod. Since the imported dashboard was hardcoding the UID from the source instance, Grafana could not resolve the datasource. Instead of throwing an error, it simply returned empty results.
The second issue was the schema version. The exported dashboard was schema version 38 (Grafana 10.x), while the target instance was running Grafana 9.5.x which expected schema version 36. Some panel types and field configurations were incompatible, and Grafana silently degraded rather than alerting me.
I confirmed this by examining the import response in the browser developer console:
// From browser console during import
POST /api/dashboards/import
Response: {
"uid": "imported-dashboard",
"version": 1,
"overwrite": false,
"message": "Dashboard imported",
"meta": {
"schemaVersion": 38, // Target expected 36
"imported": true
}
}The import succeeded at the API level, but the datasource mismatch meant panels could not query anything.
Solution
I wrote a Python script to remap datasource UIDs during the import process. The script reads the exported JSON, finds all datasource references, and replaces the UID with the correct one for the target environment:
import json
import sys
UID_MAP = {
"PBFA97CFB590B2093": "prometheus-prod",
"DD4F42E96E9E99A8": "loki-prod",
}
def remap_dashboard(input_file, output_file):
with open(input_file, "r") as f:
dashboard = json.load(f)
def remap_datasource(obj):
if isinstance(obj, dict):
if "datasource" in obj and isinstance(obj["datasource"], dict):
uid = obj["datasource"].get("uid", "")
if uid in UID_MAP:
obj["datasource"]["uid"] = UID_MAP[uid]
for key, value in obj.items():
remap_datasource(value)
elif isinstance(obj, list):
for item in obj:
remap_datasource(item)
remap_datasource(dashboard)
with open(output_file, "w") as f:
json.dump(dashboard, f, indent=2)
print(f"Remapped dashboard saved to {output_file}")
if __name__ == "__main__":
remap_dashboard(sys.argv[1], sys.argv[2])Usage:
python remap_datasource.py exported-dashboard.json remapped-dashboard.jsonAfter remapping the UIDs, I imported the fixed JSON and every panel loaded correctly. The schema version difference did not cause any visible issues after the datasource mapping was corrected β Grafana 9.5.x handled the newer schema gracefully enough for my dashboard.
For ongoing maintenance, I also added a datasource variable to the dashboard so that it uses a template variable for datasource selection instead of hardcoded UIDs:
{
"templating": {
"list": [
{
"name": "datasource",
"type": "datasource",
"query": "prometheus",
"current": {
"text": "prometheus-prod",
"value": "prometheus-prod"
}
}
]
}
}Lessons Learned
- Datasource UIDs are not portable β When exporting dashboards between Grafana instances, always check that datasource UIDs match or use template variables instead of hardcoded references.
- Grafana silently fails β A successful import response does not mean the dashboard is functional. Always verify panels after importing.
- Schema version mismatches matter β Keeping Grafana versions in sync across environments prevents subtle compatibility issues. If you cannot upgrade, at least be aware of the schema version gap.
- Automate the remapping β If you regularly move dashboards between environments, invest in a script to handle datasource UID mapping. It saves hours of manual debugging.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.