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_DATETIME

View on GitHub (pinned to ca79fe988d)

Solutions

  1. Check the embedded key name and inspect the raw response with curl using the same token
  2. Verify the API token has D1 permissions and the database_id exists
  3. Validate SQL syntax — malformed statements can return error envelopes
  4. 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

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


AI-assisted analysis of getredash/redash@ca79fe988d (2026-08-28). Data as JSON: /api/errors/97b22ae5fe401eb6. Report an issue: GitHub.