Skip to content

Commit 1957859

Browse files
committed
fix: preserve original provider error message instead of generic text
Extract message from SDK .body attribute or parse embedded dict, and add break-all to prevent long error strings from overflowing.
1 parent 6e23717 commit 1957859

2 files changed

Lines changed: 70 additions & 69 deletions

File tree

backend/app/component/error_format.py

Lines changed: 69 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,37 @@
1212
# limitations under the License.
1313
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
1414

15+
import ast
1516
import json
1617
import re
1718

1819

20+
def _parse_dict(text: str) -> dict | None:
21+
"""Parse a dict string (JSON double-quotes or Python single-quotes)."""
22+
for loader in (json.loads, ast.literal_eval):
23+
try:
24+
result = loader(text)
25+
if isinstance(result, dict):
26+
return result
27+
except Exception: # nosec B112
28+
continue
29+
return None
30+
31+
32+
def _extract_from_dict(d: dict) -> tuple[str | None, str | None, dict]:
33+
"""Pull message / code / error_obj from an OpenAI-shaped dict."""
34+
err = d.get("error") or d
35+
if not isinstance(err, dict):
36+
return None, None, {}
37+
error_obj = {
38+
"message": err.get("message"),
39+
"type": err.get("type"),
40+
"param": err.get("param"),
41+
"code": err.get("code"),
42+
}
43+
return err.get("message"), err.get("code"), error_obj
44+
45+
1946
def normalize_error_to_openai_format(
2047
exception: Exception,
2148
) -> tuple[str, str | None, dict | None]:
@@ -29,76 +56,50 @@ def normalize_error_to_openai_format(
2956
tuple: (message, error_code, error_object)
3057
"""
3158
raw_msg = str(exception)
32-
error_obj = None
33-
error_code = None
34-
message = raw_msg
3559

36-
# Match "Error code: <code> - {json}"
60+
# 1) Structured attributes (OpenAI SDK exceptions expose .body)
61+
body = getattr(exception, "body", None)
62+
if isinstance(body, dict):
63+
msg, code, obj = _extract_from_dict(body)
64+
if msg:
65+
return msg, code, obj
66+
67+
# 2) Parse "Error code: <status> - {dict}" from str(exception)
3768
m = re.search(r"Error code:\s*(\d+)\s*-\s*(\{.*\})", raw_msg, re.DOTALL)
3869
if m:
39-
error_code = m.group(1)
40-
try:
41-
parsed = json.loads(m.group(2))
42-
err = parsed.get("error") or parsed
43-
if isinstance(err, dict):
44-
error_obj = {
45-
"message": err.get("message"),
46-
"type": err.get("type"),
47-
"param": err.get("param"),
48-
"code": err.get("code"),
49-
}
50-
if err.get("message"):
51-
message = err.get("message")
52-
if err.get("code"):
53-
error_code = err.get("code")
54-
except Exception:
55-
pass
70+
parsed = _parse_dict(m.group(2))
71+
if parsed:
72+
msg, code, obj = _extract_from_dict(parsed)
73+
if msg:
74+
return msg, code or m.group(1), obj
5675

57-
# Heuristics if not parsed
58-
if error_obj is None:
59-
lower = raw_msg.lower()
60-
if (
61-
"invalid_api_key" in lower
62-
or "incorrect api key" in lower
63-
or "unauthorized" in lower
64-
or " 401" in lower
65-
):
66-
error_code = "invalid_api_key"
67-
message = "Invalid key. Validation failed."
68-
error_obj = {
69-
"message": message,
70-
"type": "invalid_request_error",
71-
"param": None,
72-
"code": "invalid_api_key",
73-
}
74-
elif (
75-
"model_not_found" in lower
76-
or "does not exist" in lower
77-
or " 404" in lower
78-
):
79-
error_code = "model_not_found"
80-
message = "Invalid model name. Validation failed."
81-
error_obj = {
82-
"message": message,
83-
"type": "invalid_request_error",
84-
"param": None,
85-
"code": "model_not_found",
86-
}
87-
elif (
88-
"insufficient_quota" in lower
89-
or "quota" in lower
90-
or " 429" in lower
91-
):
92-
error_code = "insufficient_quota"
93-
message = (
94-
"You exceeded your current quota, "
95-
"please check your plan and billing details."
96-
)
97-
error_obj = {
98-
"message": message,
99-
"type": "insufficient_quota",
100-
"param": None,
101-
"code": "insufficient_quota",
102-
}
76+
# 3) Keyword heuristics — classify the error but preserve original text
77+
lower = raw_msg.lower()
78+
if (
79+
"invalid_api_key" in lower
80+
or "incorrect api key" in lower
81+
or "unauthorized" in lower
82+
or " 401" in lower
83+
):
84+
code = "invalid_api_key"
85+
elif (
86+
"model_not_found" in lower
87+
or "does not exist" in lower
88+
or " 404" in lower
89+
):
90+
code = "model_not_found"
91+
elif "insufficient_quota" in lower or "quota" in lower or " 429" in lower:
92+
code = "insufficient_quota"
93+
else:
94+
return raw_msg, None, None
10395

104-
return message, error_code, error_obj
96+
return (
97+
raw_msg,
98+
code,
99+
{
100+
"message": raw_msg,
101+
"type": "invalid_request_error",
102+
"param": None,
103+
"code": code,
104+
},
105+
)

src/components/ChatBox/MessageItem/ModelErrorCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function ModelErrorCard({ content, errorCode }: ModelErrorCardProps) {
5454
{description}
5555
</p>
5656
{errorCode && content && content !== description && (
57-
<p className="mt-0 font-inter text-xs text-text-secondary">
57+
<p className="mt-0 break-all font-inter text-xs text-text-secondary">
5858
{content}
5959
</p>
6060
)}

0 commit comments

Comments
 (0)