Summary
The high-level Calls and Goals wrappers call response.json() before mapping unsuccessful responses into the SDK error hierarchy. An HTML/plain-text gateway response therefore leaks json.JSONDecodeError; applications catching CalleAPIError cannot inspect the HTTP status through that public contract.
Environment
- SDK: 0.7.1 source, current main
9f69e4ad3be34e4fc9e800f82e632cd45fd2da59 (rechecked September 13, 2026)
- Python: 3.13
- Package manager: pip in an isolated virtual environment
- Runtime: Windows; fully local
httpx.MockTransport
Reproduction
import httpx
from calle import CalleClient
from calle.errors import CalleAPIError
def handler(request: httpx.Request) -> httpx.Response:
return httpx.Response(502, text="<html>Bad Gateway</html>")
with httpx.Client(
base_url="https://api.heycall-e.com",
transport=httpx.MockTransport(handler),
) as http_client:
client = CalleClient(api_key="key_test", http_client=http_client)
try:
client.calls.get("call_123")
except CalleAPIError as error:
print(error.status_code) # expected 502; currently not reached
Replacing client.calls.get("call_123") with client.goals.get("goal_123") produces the same failure. No live API request or phone call is made.
Actual result
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
The exception is outside CalleAPIError/CalleConnectionError, so the status is unavailable through the documented SDK error path.
Expected result
- Non-JSON 4xx/5xx: an SDK API error retaining
status_code.
- Malformed 2xx JSON:
CalleConnectionError.
- Calls and Goals use the same decoding/error-mapping behavior.
Proposed fix and verification
The attached focused patch decodes once through a shared helper, preserves the existing API-error fallback, updates the changelog, and adds four regression cases covering both surfaces and status classes. It applies independently to the main commit above.
Fresh local verification on September 13:
pytest: 55 passed in 10.30s
ruff check src/calle tests/test_response_errors.py: passed
mypy src/calle: passed (6 source files)
git diff --cached --check: passed
Disclosure: this report, reproduction, tests, and proposed patch were prepared with substantial assistance from OpenAI Codex under the account holder's direction. No production API experience or live call is claimed.
The attachment has a .txt suffix for GitHub compatibility; its contents are a standard git patch.
CALL-E-sdk-response-errors.patch.txt
Summary
The high-level Calls and Goals wrappers call
response.json()before mapping unsuccessful responses into the SDK error hierarchy. An HTML/plain-text gateway response therefore leaksjson.JSONDecodeError; applications catchingCalleAPIErrorcannot inspect the HTTP status through that public contract.Environment
9f69e4ad3be34e4fc9e800f82e632cd45fd2da59(rechecked September 13, 2026)httpx.MockTransportReproduction
Replacing
client.calls.get("call_123")withclient.goals.get("goal_123")produces the same failure. No live API request or phone call is made.Actual result
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)The exception is outside
CalleAPIError/CalleConnectionError, so the status is unavailable through the documented SDK error path.Expected result
status_code.CalleConnectionError.Proposed fix and verification
The attached focused patch decodes once through a shared helper, preserves the existing API-error fallback, updates the changelog, and adds four regression cases covering both surfaces and status classes. It applies independently to the main commit above.
Fresh local verification on September 13:
Disclosure: this report, reproduction, tests, and proposed patch were prepared with substantial assistance from OpenAI Codex under the account holder's direction. No production API experience or live call is claimed.
The attachment has a
.txtsuffix for GitHub compatibility; its contents are a standard git patch.CALL-E-sdk-response-errors.patch.txt