From 9fd0ce1516a0d352034283cdf2eac72bc5271aff Mon Sep 17 00:00:00 2001 From: IluaAir Date: Fri, 17 Apr 2026 20:45:16 +0300 Subject: [PATCH] Implement enhanced error detection for NodeNotFound in OxiAPIError - Added a new helper function `_looks_like_node_not_found_html` to identify NodeNotFound errors based on HTTP response content. - Updated the `from_http_error` method in the `OxiAPIError` class to convert 500 status codes to 404 when a NodeNotFound error is detected, improving error handling and user feedback. --- oxi/exception.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/oxi/exception.py b/oxi/exception.py index 614627d..be3adaf 100644 --- a/oxi/exception.py +++ b/oxi/exception.py @@ -13,10 +13,32 @@ _STATUS_MESSAGES: dict[int, str] = { } +def _looks_like_node_not_found_html(e: "HTTPError") -> bool: + resp = getattr(e, "response", None) + if resp is None: + return False + try: + content_type = (resp.headers or {}).get("Content-Type", "") + except Exception: + content_type = "" + if "text/html" not in (content_type or "").lower(): + return False + try: + body = (resp.text or "")[:20_000] + except Exception: + return False + return ( + "Oxidized::NodeNotFound" in body + or "NodeNotFound" in body + or "Oxidized::NodeNotFound" in body + ) + + class OxiAPIError(Exception): def __init__(self, message: str, status_code: Optional[int] = None): super().__init__(message) self.status_code = status_code + self.message = message def __str__(self): if self.status_code is not None: @@ -25,10 +47,19 @@ class OxiAPIError(Exception): @classmethod def from_http_error(cls, e: "HTTPError", context: str = "") -> "OxiAPIError": - status = e.response.status_code + resp = getattr(e, "response", None) + status = resp.status_code if resp is not None else None + + if status == 500 and _looks_like_node_not_found_html(e): + status = 404 + if status == 404: message = f"{context} not found" if context else "Not found" else: - base = _STATUS_MESSAGES.get(status) or e.response.reason or f"HTTP {status}" + base = ( + (_STATUS_MESSAGES.get(status) if status is not None else None) + or (resp.reason if resp is not None else None) + or (f"HTTP {status}" if status is not None else "Request failed") + ) message = f"{context}: {base}" if context else base return cls(message, status)