Skip to content

Commit 3e7856d

Browse files
committed
Merge branch 'main' into release/current
2 parents a7410e1 + 06a9c3a commit 3e7856d

File tree

2 files changed

+162
-11
lines changed

2 files changed

+162
-11
lines changed

pyobas/client.py

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
REDIRECT_MSG = (
1010
"pyobas detected a {status_code} ({reason!r}) redirection. You must update "
11-
"your OpenBVAS URL to the correct URL to avoid issues. The redirection was from: "
11+
"your OpenBAS URL to the correct URL to avoid issues. The redirection was from: "
1212
"{source!r} to {target!r}"
1313
)
1414

@@ -203,25 +203,95 @@ def http_request(
203203
if 200 <= result.status_code < 300:
204204
return result.response
205205

206-
error_message = result.content
206+
# Extract a meaningful error message from the server response
207+
error_message: Any = None
208+
209+
# First, try to get the raw text content
207210
try:
208-
error_json = result.json()
209-
for k in ("message", "error"):
210-
if k in error_json:
211-
error_message = error_json[k]
212-
except (KeyError, ValueError, TypeError):
211+
raw_text = result.content.decode("utf-8", errors="ignore").strip()
212+
# If it's a simple text message (not JSON), use it directly
213+
if (
214+
raw_text
215+
and not raw_text.startswith("{")
216+
and not raw_text.startswith("[")
217+
):
218+
error_message = raw_text[:500]
219+
except Exception:
213220
pass
214221

222+
# If we don't have a message yet, try JSON parsing
223+
if not error_message:
224+
try:
225+
error_json = result.json()
226+
# Common fields
227+
if isinstance(error_json, dict):
228+
# First priority: look for a 'message' field (most specific)
229+
if "message" in error_json:
230+
error_message = error_json.get("message")
231+
elif "execution_message" in error_json:
232+
error_message = error_json.get("execution_message")
233+
elif "error" in error_json:
234+
err = error_json.get("error")
235+
if isinstance(err, dict) and "message" in err:
236+
error_message = err.get("message")
237+
elif err and err not in [
238+
"Internal Server Error",
239+
"Bad Request",
240+
"Not Found",
241+
"Unauthorized",
242+
"Forbidden",
243+
]:
244+
# Only use 'error' field if it's not a generic HTTP status
245+
error_message = str(err)
246+
elif "errors" in error_json:
247+
errs = error_json.get("errors")
248+
if isinstance(errs, list) and errs:
249+
# Join any messages in the list
250+
messages = []
251+
for item in errs:
252+
if isinstance(item, dict) and "message" in item:
253+
messages.append(str(item.get("message")))
254+
else:
255+
messages.append(str(item))
256+
error_message = "; ".join(messages)
257+
elif isinstance(error_json, str):
258+
error_message = error_json
259+
# Fallback to serialized json if we still have nothing
260+
if not error_message:
261+
error_message = utils.json_dumps(error_json)[:500]
262+
except Exception:
263+
# If JSON parsing fails, use the raw text we might have
264+
if not error_message:
265+
try:
266+
error_message = result.response.text[:500]
267+
except Exception:
268+
try:
269+
error_message = result.content.decode(errors="ignore")[
270+
:500
271+
]
272+
except Exception:
273+
error_message = str(result.content)[:500]
274+
275+
# If still no message or a generic HTTP status, use status text
276+
if not error_message or error_message == result.response.reason:
277+
error_message = result.response.reason or "Unknown error"
278+
215279
if result.status_code == 401:
216280
raise exceptions.OpenBASAuthenticationError(
217281
response_code=result.status_code,
218-
error_message=error_message,
282+
error_message=error_message or "Authentication failed",
219283
response_body=result.content,
220284
)
221285

286+
# Use the extracted error message, not the HTTP reason
287+
final_error_message = error_message
288+
if not final_error_message or final_error_message == result.response.reason:
289+
# Only use HTTP reason as last resort
290+
final_error_message = result.response.reason or "Unknown error"
291+
222292
raise exceptions.OpenBASHttpError(
223293
response_code=result.status_code,
224-
error_message=error_message,
294+
error_message=final_error_message,
225295
response_body=result.content,
226296
)
227297

pyobas/exceptions.py

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,90 @@ def __init__(
2626
self.error_message = error_message
2727

2828
def __str__(self) -> str:
29+
# Start with the provided error message
30+
message = self.error_message
31+
32+
# List of generic HTTP status messages that indicate we should look deeper
33+
generic_messages = (
34+
"Internal Server Error",
35+
"Bad Request",
36+
"Not Found",
37+
"Unauthorized",
38+
"Forbidden",
39+
"Service Unavailable",
40+
"Gateway Timeout",
41+
"Unknown error",
42+
)
43+
44+
# Only try to extract from response body if message is truly generic
45+
# Don't override if we already have a specific error message
46+
if (
47+
not message or (message in generic_messages and len(message) < 30)
48+
) and self.response_body is not None:
49+
try:
50+
import json
51+
52+
body = self.response_body.decode(errors="ignore")
53+
data = json.loads(body)
54+
extracted_msg = None
55+
56+
if isinstance(data, dict):
57+
# Try various common error fields
58+
if "error" in data:
59+
err = data.get("error")
60+
if isinstance(err, dict) and "message" in err:
61+
extracted_msg = err.get("message")
62+
elif isinstance(err, str):
63+
extracted_msg = err
64+
elif "message" in data:
65+
extracted_msg = data.get("message")
66+
elif "execution_message" in data:
67+
extracted_msg = data.get("execution_message")
68+
elif "detail" in data:
69+
extracted_msg = data.get("detail")
70+
elif "errors" in data:
71+
errs = data.get("errors")
72+
if isinstance(errs, list) and errs:
73+
# Join any messages in the list
74+
parts = []
75+
for item in errs:
76+
if isinstance(item, dict) and "message" in item:
77+
parts.append(str(item.get("message")))
78+
else:
79+
parts.append(str(item))
80+
extracted_msg = "; ".join(parts)
81+
elif isinstance(errs, str):
82+
extracted_msg = errs
83+
84+
# Use extracted message if it's better than what we have
85+
if extracted_msg and extracted_msg not in generic_messages:
86+
message = str(extracted_msg)
87+
elif not message:
88+
# Last resort: use the raw body
89+
message = body[:500]
90+
91+
except json.JSONDecodeError:
92+
# Not JSON, use raw text if we don't have a good message
93+
if not message or message in generic_messages:
94+
try:
95+
decoded = self.response_body.decode(errors="ignore")[:500]
96+
if decoded and decoded not in generic_messages:
97+
message = decoded
98+
except Exception:
99+
pass
100+
except Exception:
101+
pass
102+
103+
# Final fallback
104+
if not message:
105+
message = "Unknown error"
106+
107+
# Clean up the message - remove extra whitespace and newlines
108+
message = " ".join(message.split())
109+
29110
if self.response_code is not None:
30-
return f"{self.response_code}: {self.error_message}"
31-
return f"{self.error_message}"
111+
return f"{self.response_code}: {message}"
112+
return f"{message}"
32113

33114

34115
class OpenBASAuthenticationError(OpenBASError):

0 commit comments

Comments
 (0)