{"record":{"id":"aec1a6b3828812d0","repo":"nexu-io/open-design","slug":"request-failed-with-no-error-details","errorCode":null,"errorMessage":"Request failed with no error details","messagePattern":"Request failed with no error details","errorType":"http","errorClass":"HTTPError","httpStatus":null,"severity":"error","filePath":"design-templates/last30days/scripts/lib/http.py","lineNumber":151,"sourceCode":"        except urllib.error.URLError as e:\n            log(f\"URL Error: {e.reason}\")\n            last_error = HTTPError(f\"URL Error: {e.reason}\")\n            if attempt < retries - 1:\n                time.sleep(RETRY_DELAY * (attempt + 1))\n        except json.JSONDecodeError as e:\n            log(f\"JSON decode error: {e}\")\n            last_error = HTTPError(f\"Invalid JSON response: {e}\")\n            raise last_error\n        except (OSError, TimeoutError, ConnectionResetError) as e:\n            # Handle socket-level errors (connection reset, timeout, etc.)\n            log(f\"Connection error: {type(e).__name__}: {e}\")\n            last_error = HTTPError(f\"Connection error: {type(e).__name__}: {e}\")\n            if attempt < retries - 1:\n                time.sleep(RETRY_DELAY * (attempt + 1))\n\n    if last_error:\n        raise last_error\n    raise HTTPError(\"Request failed with no error details\")\n\n\ndef get(url: str, headers: Optional[Dict[str, str]] = None, **kwargs) -> Dict[str, Any]:\n    \"\"\"Make a GET request.\"\"\"\n    return request(\"GET\", url, headers=headers, **kwargs)\n\n\ndef post(url: str, json_data: Dict[str, Any], headers: Optional[Dict[str, str]] = None, **kwargs) -> Dict[str, Any]:\n    \"\"\"Make a POST request with JSON body.\"\"\"\n    return request(\"POST\", url, headers=headers, json_data=json_data, **kwargs)\n\n\ndef post_raw(url: str, json_data: Dict[str, Any], headers: Optional[Dict[str, str]] = None, **kwargs) -> str:\n    \"\"\"Make a POST request with JSON body and return raw text.\"\"\"\n    return request(\"POST\", url, headers=headers, json_data=json_data, raw=True, **kwargs)\n\n\ndef scrapecreators_headers(token: str) -> Dict[str, str]:","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/design-templates/last30days/scripts/lib/http.py#L133-L169","documentation":"Defensive tail of the retry loop in http.request(): the loop attempts up to `retries` times, sets last_error on HTTPError / JSON / connection failures, and returns on success. If the loop exits without ever returning and without ever assigning last_error, control reaches `raise HTTPError(\"Request failed with no error details\")`. This is an unreachable-in-practice fallback: a real failure would have populated last_error. Hitting it usually means retries <= 0 (loop body never ran), a non-OSError/non-HTTPError exception path, or a successful-looking response that failed to return.","triggerScenarios":"Calling request() with retries <= 0 such that the for-loop body never executes; an upstream code path that swallowed the exception before last_error was set; a future refactor that adds a return-free success branch.","commonSituations":"A caller passing retries=0 (or negative) expecting at least one attempt; monkeypatched/mocked transport that returns None instead of raising; a Python version/stdlib change introducing a new exception type not caught by the existing handlers.","solutions":["Pass retries >= 1 so the loop body runs at least once (a real failure then populates last_error with a useful message).","When catching HTTPError, also log `last_error` / the response status so the no-details fallback is never the only signal.","If you control the transport, ensure it either returns a parsed dict or raises HTTPError/OSError — never returns None.","Audit the except clauses if a new exception type appears (e.g. ssl.SSLError) and add it to the handlers so last_error is set."],"exampleFix":"# before\nresp = http.request(\"GET\", url, retries=0)\n# after\nresp = http.request(\"GET\", url, retries=3)","handlingStrategy":"validation","validationCode":"if retries < 1:\n    raise ValueError(f'retries must be >= 1 (got {retries}); the loop must run at least once')","typeGuard":null,"tryCatchPattern":"from lib.http import HTTPError\ntry:\n    data = http.request('GET', url, retries=3)\nexcept HTTPError as e:\n    if str(e) == 'Request failed with no error details':\n        # defensive fallback hit; surface transport state for debugging\n        log('http.request exhausted retries without a captured cause')\n    raise","preventionTips":["Always pass retries >= 1.","Ensure the transport either returns a parsed dict or raises HTTPError/OSError.","When adding support for a new stdlib exception, extend the except tuple so last_error is set."],"tags":["network","http","python","defensive-fallback","retry"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}