Skip to content

High-level Calls and Goals leak JSONDecodeError for non-JSON API responses #39

Description

@cuentapraces07-ops

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions