{"record":{"id":"113ee63aaba1f1c2","repo":"getredash/redash","slug":"invalid-json-response-from-d1-str-e","errorCode":null,"errorMessage":"Invalid JSON response from D1: {str(e)}","messagePattern":"Invalid JSON response from D1: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"redash/query_runner/d1.py","lineNumber":87,"sourceCode":"            \"Content-Type\": \"application/json\",\n        }\n        body = {\"sql\": sql, \"params\": params or []}\n\n        try:\n            resp = session.post(self.configuration.get(\"cf_url\"), headers=headers, data=json.dumps(body), timeout=30)\n            resp.raise_for_status()\n            data = resp.json()\n\n            # Expected: { \"result\": [ { \"results\": [...] } ] }\n            results = data.get(\"result\", [])\n            if not results:\n                return []\n            return results[0].get(\"results\", [])\n\n        except session.exceptions.RequestException as e:\n            raise Exception(f\"Failed to connect to Cloudflare D1: {str(e)}\")\n        except json.JSONDecodeError as e:\n            raise Exception(f\"Invalid JSON response from D1: {str(e)}\")\n        except KeyError as e:\n            raise Exception(f\"Unexpected response format from D1: {str(e)}\")\n\n    def run_query(self, query, user):\n        try:\n            rows = self._query(query)\n            if not rows:\n                return {\"columns\": [], \"rows\": []}, None\n\n            # Infer columns from first row\n            first_row = rows[0]\n            columns = []\n            for k, v in first_row.items():\n                # Get the Python type name and map it to Redash type\n                python_type = type(v).__name__\n                redash_type = TYPES_MAP.get(python_type, TYPE_STRING)\n\n                # Special handling for strings that look like datetimes","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/getredash/redash/blob/ca79fe988d81cdac9675b412f3dfcab107bc1fbc/redash/query_runner/d1.py#L69-L105","documentation":"D1 runner's _query catches json.JSONDecodeError when the Cloudflare API response body isn't valid JSON and re-raises with the parse detail. The HTTP call succeeded but the body couldn't be parsed.","triggerScenarios":"The D1 endpoint returning HTML (a proxy/CDN error page), an empty body, or truncated output that json.loads can't parse.","commonSituations":"Corporate proxy returning an HTML block page, Cloudflare serving a non-JSON error page during incidents, or a base URL pointing at a non-API host.","solutions":["Reproduce the request with curl and inspect the raw body","Fix proxy/WAF rules that rewrite responses from api.cloudflare.com","Confirm the request URL is the official API endpoint","Retry during a Cloudflare incident if the body is an error page"],"exampleFix":"# before\nBASE_URL = \"https://api.cloudflare.com\"\n\n# after\nBASE_URL = \"https://api.cloudflare.com/client/v4\"","handlingStrategy":"try-catch","validationCode":"import requests\nr = requests.post(url, headers=h, json=body, timeout=10)\nr.raise_for_status()\nassert r.headers.get('content-type', '').startswith('application/json'), 'proxy is mangling responses'","typeGuard":null,"tryCatchPattern":"try:\n    rows = d1_runner._query(q)\nexcept Exception as e:\n    if 'Invalid JSON response' in str(e):\n        inspect_raw_body_with_curl(); flag_proxy_config()","preventionTips":["Exclude API domains from TLS-intercepting proxies","Alert on content-type mismatches from Cloudflare responses","Retry during Cloudflare incident windows"],"tags":["redash","cloudflare-d1","json","response-parsing"],"backgroundTag":"invalid-json-response","analyzedSha":"ca79fe988d81cdac9675b412f3dfcab107bc1fbc","analyzedAt":"2026-08-28T18:32:34.637Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}