getredash/redash · error · Exception
Unexpected response format from D1: {str(e)}
Error message
Unexpected response format from D1: {str(e)} What it means
D1 runner's _query catches KeyError while drilling into the response envelope — typically 'result' missing from the JSON or results[0] lacking 'results' — and re-raises as 'Unexpected response format from D1'. The API replied with JSON but not the expected success shape.
Source
Thrown at redash/query_runner/d1.py:89
body = {"sql": sql, "params": params or []}
try:
resp = session.post(self.configuration.get("cf_url"), headers=headers, data=json.dumps(body), timeout=30)
resp.raise_for_status()
data = resp.json()
# Expected: { "result": [ { "results": [...] } ] }
results = data.get("result", [])
if not results:
return []
return results[0].get("results", [])
except session.exceptions.RequestException as e:
raise Exception(f"Failed to connect to Cloudflare D1: {str(e)}")
except json.JSONDecodeError as e:
raise Exception(f"Invalid JSON response from D1: {str(e)}")
except KeyError as e:
raise Exception(f"Unexpected response format from D1: {str(e)}")
def run_query(self, query, user):
try:
rows = self._query(query)
if not rows:
return {"columns": [], "rows": []}, None
# Infer columns from first row
first_row = rows[0]
columns = []
for k, v in first_row.items():
# Get the Python type name and map it to Redash type
python_type = type(v).__name__
redash_type = TYPES_MAP.get(python_type, TYPE_STRING)
# Special handling for strings that look like datetimes
if python_type == "str" and detect_datetime_string(v):
redash_type = TYPE_DATETIMEView on GitHub (pinned to ca79fe988d)
Solutions
- Check the embedded key name and inspect the raw response with curl using the same token
- Verify the API token has D1 permissions and the database_id exists
- Validate SQL syntax — malformed statements can return error envelopes
- Update Redash if Cloudflare changed the D1 response schema
Example fix
# before
results = data.get("result", [])
return results[0].get("results", [])
# after
if not data.get("success"):
raise Exception(f"D1 API error: {data.get('errors')}")
results = data.get("result", [])
return results[0].get("results", []) if results else [] Defensive patterns
Strategy: validation
Validate before calling
import requests
r = requests.post(url, headers=h, json=body, timeout=10)
data = r.json()
assert data.get('success'), data.get('errors') # check envelope before relying on 'result' Type guard
def is_d1_success_envelope(data: dict) -> bool:
return isinstance(data, dict) and data.get('success') is True and 'result' in data Try / catch
try:
rows = d1_runner._query(q)
except Exception as e:
if 'Unexpected response format' in str(e):
errors = fetch_raw_api_errors(); route_token_or_sql_fix(errors) Prevention
- Check the success flag on every Cloudflare API response in custom code
- Rotate D1 tokens before expiry with calendar alerts
- Re-verify database_id after workspace reorganizations
When it happens
Trigger: Cloudflare returns a JSON error envelope like {"success": false, "errors": [...]} with no 'result' key (bad API token, nonexistent database, malformed SQL), so the results[0] lookup chain KeyErrors.
Common situations: Revoked or insufficient API token, deleted or wrong database_id, or an API version change altering the envelope shape.
Related errors
- Failed to connect to Cloudflare D1: {str(e)}
- Invalid JSON response from D1: {str(e)}
- Failed to get schema: {str(e)}
- Invalid JWT token
- Error during query execution. Reason: {error}
AI-assisted analysis of getredash/redash@ca79fe988d (2026-08-28).
Data as JSON: /api/errors/97b22ae5fe401eb6.
Report an issue: GitHub.