{"record":{"id":"ee62f2af9a5e5a7f","repo":"abi/screenshot-to-code","slug":"invalid-prediction-status-response","errorCode":null,"errorMessage":"Invalid prediction status response.","messagePattern":"Invalid prediction status response\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/image_generation/replicate.py","lineNumber":61,"sourceCode":"def _extract_prediction_id(response_json: Mapping[str, Any]) -> str:\n    prediction_id = response_json.get(\"id\")\n    if not isinstance(prediction_id, str) or not prediction_id:\n        raise ValueError(\"Prediction ID not found in initial response.\")\n    return prediction_id\n\n\nasync def _poll_prediction(\n    client: httpx.AsyncClient, prediction_id: str, headers: dict[str, str]\n) -> dict[str, Any]:\n    status_check_url = f\"{REPLICATE_API_BASE_URL}/predictions/{prediction_id}\"\n\n    for _ in range(MAX_POLLS):\n        await asyncio.sleep(POLL_INTERVAL_SECONDS)\n        status_response = await client.get(status_check_url, headers=headers)\n        status_response.raise_for_status()\n        status_response_raw: Any = status_response.json()\n        if not isinstance(status_response_raw, dict):\n            raise ValueError(\"Invalid prediction status response.\")\n        status_response_json = cast(dict[str, Any], status_response_raw)\n\n        status = status_response_json.get(\"status\")\n        if status == \"succeeded\":\n            return cast(dict[str, Any], status_response_json)\n        if status == \"error\":\n            error_message = str(status_response_json.get(\"error\", \"Unknown error\"))\n            raise ValueError(f\"Inference errored out: {error_message}\")\n        if status == \"failed\":\n            raise ValueError(\"Inference failed\")\n\n    raise TimeoutError(\"Inference timed out\")\n\n\nasync def _run_prediction(\n    endpoint_url: str, payload: dict[str, Any], api_token: str\n) -> Any:\n    headers = _build_headers(api_token)","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/abi/screenshot-to-code/blob/d026163f586dfa8c5c10d28c36edd59a9d3b0e88/backend/image_generation/replicate.py#L43-L79","documentation":"During prediction polling in the Replicate client, _poll_prediction raises ValueError(\"Invalid prediction status response\") when the GET to /predictions/{id} succeeds at the HTTP level but the parsed JSON body is not an object (dict). This guards against HTML error pages from proxies, JSON arrays, or string bodies that httpx's .json() happily parses but that carry no `status` field.","triggerScenarios":"Polling GET /predictions/{id} returns 200 with body \"null\", a JSON array, a bare string/number, or a proxy-injected HTML page that happens to parse as JSON scalar. Any non-dict JSON triggers it immediately, aborting the poll loop.","commonSituations":"Reverse proxies or CDNs intercepting long-running polls; Replicate returning an unexpected envelope during incidents; API version drift.","solutions":["Capture and log status_response.text when the body is not a dict to identify the interceptor.","Ensure direct egress to api.replicate.com without a rewriting proxy.","If the shape legitimately changed, adapt the isinstance check to the new envelope."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"from typing import Any, Mapping\n\ndef is_prediction_status_payload(body: Any) -> bool:\n    return isinstance(body, Mapping) and \"status\" in body","tryCatchPattern":"try:\n    output = await call_replicate_model(model, input, token)\nexcept ValueError as e:\n    if \"Invalid prediction status response\" in str(e):\n        log.error(\"Polling got a non-dict body — proxy interference likely\")\n    raise","preventionTips":["Ensure direct HTTPS egress to api.replicate.com without body-rewriting proxies","Monitor for this specific message to detect interception early","Keep httpx updated so JSON parsing behavior matches the API"],"tags":["replicate","api","image-generation","polling","response-validation"],"backgroundTag":null,"analyzedSha":"d026163f586dfa8c5c10d28c36edd59a9d3b0e88","analyzedAt":"2026-08-14T22:02:06.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}