{"record":{"id":"d0073ce8557d58e7","repo":"huggingface/open-r1","slug":"empty-response-status-status","errorCode":null,"errorMessage":"Empty response. status={status}","messagePattern":"Empty response\\. status=(.+?)","errorType":"exception","errorClass":"PistonError","httpStatus":null,"severity":"error","filePath":"src/open_r1/utils/competitive_programming/piston_client.py","lineNumber":162,"sourceCode":"\n        status = None\n        endpoint = None\n\n        for attempt in range(max_retries + 1):\n            try:\n                endpoint = await self._wait_for_endpoint()\n                if attempt > 0:\n                    await asyncio.sleep(1)\n                async with self.session.post(\n                    f\"{endpoint.rstrip('/')}/execute\", json=data, headers={\"Content-Type\": \"application/json\"}\n                ) as response:\n                    status = response.status\n                    res_json = await response.json(content_type=None)\n\n                    if status != 200:\n                        raise PistonError(f\"Server error. status={status}. {res_json}\")\n                    if res_json is None:\n                        raise PistonError(f\"Empty response. status={status}\")\n                    # piston overloaded\n                    if \"run\" in res_json and \"Resource temporarily unavailable\" in res_json[\"run\"].get(\"stderr\", \"\"):\n                        raise PistonError(f\"Piston overloaded: {res_json['run']['stderr']}\")\n                    return res_json\n\n            except (PistonError, asyncio.TimeoutError, aiohttp.ClientConnectionError, RuntimeError) as e:\n                # Only retry if we haven't reached max retries yet\n                if attempt < max_retries:\n                    # Calculate backoff with jitter\n                    delay = min(base_delay * (2**attempt), 10)  # Exponential backoff, capped at 10 seconds\n                    jitter = delay * 0.2 * (2 * asyncio.get_event_loop().time() % 1 - 0.5)  # Add ±10% jitter\n                    retry_delay = delay + jitter\n                    print(f\"Retrying in {retry_delay:.2f} seconds [{self.endpoint_ids[endpoint]}] {endpoint} - {e}\")\n\n                    # special case: worker died\n                    if isinstance(e, aiohttp.ClientConnectionError) and \"Connect call failed\" in str(e):\n                        await self._check_failed_endpoint(endpoint)\n                    else:","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/huggingface/open-r1/blob/1416fa0cf21595d2083b399a2a0bbddd7f6e9563/src/open_r1/utils/competitive_programming/piston_client.py#L144-L180","documentation":"PistonClient.send_execute raises PistonError('Empty response. status=...') when the HTTP status is 200 but the JSON body parses to None. This indicates a proxy or worker that returned an empty 200 body instead of a valid Piston execution result. It is treated as a retriable failure.","triggerScenarios":"response.json(content_type=None) returns None despite status==200, typically when the endpoint returns an empty body, a 204-like response, or a proxy stripping the body while keeping 200.","commonSituations":"Misconfigured reverse proxy / ingress that swallows response bodies, a health-check page or wrong service listening on the port, flaky network equipment truncating responses.","solutions":["Retry the request; the client already retries with backoff, so a persistent message means the endpoint is broken","curl the /execute endpoint and inspect the raw response body","Remove or fix the misbehaving proxy in front of Piston","Point base_endpoints at the Piston worker directly to isolate the proxy","Drop the unhealthy endpoint from the pool via the client's health checking"],"exampleFix":"// before\ncurl -s -X POST http://proxy.example.com/api/v2/execute -d '...'  # empty body\n// after\ncurl -s -X POST http://piston-worker:2000/api/v2/execute -d '...'  # returns JSON result","handlingStrategy":"try-catch","validationCode":"import aiohttp\nasync def returns_body(url, payload):\n    async with aiohttp.ClientSession() as s:\n        async with s.post(f\"{url.rstrip('/')}/execute\", json=payload) as r:\n            body = await r.json(content_type=None)\n            return r.status == 200 and body is not None","typeGuard":"def is_valid_piston_result(res_json):\n    return isinstance(res_json, dict) and \"run\" in res_json","tryCatchPattern":"try:\n    res = await client.send_execute(data)\nexcept PistonError as e:\n    if str(e).startswith(\"Empty response\"):\n        res = await client.send_execute(data)  # retry, possibly on a different endpoint\n    else:\n        raise","preventionTips":["Test each endpoint with a smoke execute request during setup","Avoid proxies that buffer/strip response bodies in front of Piston","Verify Content-Type handling; rely on the client's content_type=None parsing","Rotate out endpoints that repeatedly return empty 200s"],"tags":["http","empty-response","network","proxy"],"backgroundTag":"empty-response-body","analyzedSha":"1416fa0cf21595d2083b399a2a0bbddd7f6e9563","analyzedAt":"2026-08-30T08:56:53.400Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}