+7
-4
src/tangled_mcp/_tangled/_client.py
+7
-4
src/tangled_mcp/_tangled/_client.py
···
10
10
11
11
def _extract_error_message(e: Exception) -> str:
12
12
"""extract a clean, concise error message from an exception"""
13
-
# handle atproto Response objects with XrpcError
14
-
if hasattr(e, "content") and hasattr(e.content, "message"):
15
-
return e.content.message
13
+
# handle atproto RequestErrorBase (BadRequestError, UnauthorizedError, etc.)
14
+
# structure: e.response.content.message
15
+
if hasattr(e, "response") and e.response:
16
+
content = getattr(e.response, "content", None)
17
+
if content and hasattr(content, "message"):
18
+
return content.message
16
19
# handle httpx errors
17
20
if hasattr(e, "response") and hasattr(e.response, "text"):
18
-
return e.response.text[:200] # truncate long responses
21
+
return e.response.text[:200]
19
22
# fallback to string but limit length
20
23
msg = str(e)
21
24
if len(msg) > 200:
+43
tests/test_resolver.py
+43
tests/test_resolver.py
···
2
2
3
3
import pytest
4
4
5
+
from tangled_mcp._tangled._client import _extract_error_message
6
+
7
+
8
+
class TestExtractErrorMessage:
9
+
"""test error message extraction from various exception types"""
10
+
11
+
def test_extracts_from_atproto_response(self):
12
+
"""extracts message from atproto RequestErrorBase structure"""
13
+
14
+
class MockContent:
15
+
message = "handle must be a valid handle"
16
+
17
+
class MockResponse:
18
+
content = MockContent()
19
+
20
+
class MockException(Exception):
21
+
response = MockResponse()
22
+
23
+
msg = _extract_error_message(MockException())
24
+
assert msg == "handle must be a valid handle"
25
+
26
+
def test_truncates_long_messages(self):
27
+
"""truncates messages longer than 200 chars"""
28
+
long_msg = "x" * 300
29
+
30
+
class MockException(Exception):
31
+
pass
32
+
33
+
e = MockException(long_msg)
34
+
msg = _extract_error_message(e)
35
+
assert len(msg) == 203 # 200 + "..."
36
+
assert msg.endswith("...")
37
+
38
+
def test_handles_none_response(self):
39
+
"""handles exception with None response"""
40
+
41
+
class MockException(Exception):
42
+
response = None
43
+
44
+
e = MockException("fallback message")
45
+
msg = _extract_error_message(e)
46
+
assert msg == "fallback message"
47
+
5
48
6
49
class TestRepoIdentifierParsing:
7
50
"""test repository identifier format validation"""