{"record":{"id":"78023ec89d72777a","repo":"calesthio/OpenMontage","slug":"non-json-response-from-doubao-api-http-response","errorCode":null,"errorMessage":"Non-JSON response from Doubao API: HTTP {response.status_code}","messagePattern":"Non-JSON response from Doubao API: HTTP (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"tools/audio/doubao_tts.py","lineNumber":368,"sourceCode":"                request_id=str(uuid.uuid4()),\n                return_usage=return_usage,\n            )\n            response = requests_module.post(self.QUERY_URL, headers=headers, json={\"task_id\": task_id}, timeout=(10, 60))\n            query_data = self._json_or_raise(response)\n            self._raise_for_doubao_error(response.status_code, query_data)\n            status = query_data.get(\"data\", {}).get(\"task_status\")\n            if status == 2:\n                return query_data\n            if status == 3:\n                raise RuntimeError(f\"Doubao task failed: {query_data.get('message', 'unknown error')}\")\n        raise TimeoutError(f\"Doubao task did not finish within {timeout_seconds} seconds\")\n\n    @staticmethod\n    def _json_or_raise(response: Any) -> dict[str, Any]:\n        try:\n            return response.json()\n        except ValueError as exc:\n            raise RuntimeError(f\"Non-JSON response from Doubao API: HTTP {response.status_code}\") from exc\n\n    def _raise_for_doubao_error(self, http_status: int, payload: dict[str, Any]) -> None:\n        code = payload.get(\"code\")\n        if http_status < 400 and code == 20000000:\n            return\n        message = payload.get(\"message\", \"unknown error\")\n        hint = self._diagnostic_hint(message)\n        raise RuntimeError(f\"HTTP {http_status}, code {code}: {message}{hint}\")\n\n    @staticmethod\n    def _diagnostic_hint(message: str) -> str:\n        lowered = message.lower()\n        if \"load grant\" in lowered or \"requested grant not found\" in lowered:\n            return \" (check DOUBAO_SPEECH_API_KEY and use the new-console X-Api-Key flow)\"\n        if \"speaker permission denied\" in lowered or \"access denied\" in lowered:\n            return \" (check voice_id/DOUBAO_SPEECH_VOICE_TYPE and voice authorization)\"\n        if \"quota exceeded\" in lowered:\n            return \" (check quota, concurrency, or remaining character package)\"","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/audio/doubao_tts.py#L350-L386","documentation":"Raised by the static helper _json_or_raise when response.json() raises ValueError, i.e. the Doubao API returned a body that is not valid JSON (HTML error page, empty body, XML gateway error). It fires for both the submit call and every poll iteration, and preserves the HTTP status to distinguish WAF/gateway pages from truncated responses.","triggerScenarios":"Gateway/WAF returns an HTML 4xx/5xx page; body truncated by a proxy timeout; empty 200 response; connection reset mid-body parsed as non-JSON.","commonSituations":"Rate-limited by an edge proxy (HTML 429 page), Volcengine regional outage, corporate proxy injecting an error page, or auth failure returning a non-JSON body.","solutions":["Capture response.text (truncated) in the error to identify the gateway/proxy source","Check the HTTP status: 4xx from proxy vs 5xx from service","Retry with backoff — non-JSON gateway responses are usually transient","If persistent, bypass proxies or verify network egress to the Volcengine domain"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    result = doubao_tool.execute(inputs)\nexcept RuntimeError as e:\n    if \"Non-JSON response\" in str(e):\n        backoff_and_retry(max_attempts=3)  # gateway pages are transient\n    else:\n        raise","preventionTips":["Route Volcengine traffic outside corporate proxies that inject error pages","Set sane connect/read timeouts (the tool already uses (10,60)/(10,120))","Retry non-JSON responses with exponential backoff rather than failing the job"],"tags":["doubao","json","gateway","network"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}