Skip to content

fix(cli): map exhausted transport retries to ServerError - #13079

Merged
kodjima33 merged 1 commit into
BasedHardware:mainfrom
Aj2280:fix/cli-transport-error-contract
Sep 8, 2026
Merged

fix(cli): map exhausted transport retries to ServerError#13079
kodjima33 merged 1 commit into
BasedHardware:mainfrom
Aj2280:fix/cli-transport-error-contract

Conversation

@Aj2280

@Aj2280 Aj2280 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR maps exhausted httpx.TransportError exceptions in OmiClient._request() to ServerError(message="Connection failed", detail="Unable to reach the Omi API. Check your network connection or try again shortly."), resolving #12961.

Previously, exhausted transport retries (such as connection refused, read timeout, or protocol error) escaped _request() unmapped and fell through to the generic exception handler in main(). This caused commands to exit with code 1 and emit unformatted plain text on stderr even when --json was requested, violating the documented stable CLI exit code contract (3: server error / connection failure).

Changes

  • sdks/python-cli/omi_cli/client.py:
    • Catch httpx.TransportError after the retry loop in OmiClient._request().
    • Raise ServerError(message="Connection failed", detail="Unable to reach the Omi API. Check your network connection or try again shortly.") from exc.
    • Preserves retries, recovery on transient errors, and chains the underlying exception as __cause__.
  • sdks/python-cli/tests/test_client_retry.py:
    • Added regression test verifying ConnectError exhausts 4 retries and raises ServerError (exit code 3).
    • Added regression test for ReadTimeout.
    • Added regression test for ProtocolError.
    • Added test verifying transient ConnectError recovers on retry and returns data.
    • Added tests verifying main() exits with code 3 in both plain and --json modes and outputs the expected error structures to stderr.

Verification

  • sdks/python-cli/venv/bin/pytest sdks/python-cli/tests/: all 134 passed, 1 skipped.
  • Real loopback reproduction:
    • omi --api-base http://127.0.0.1:54321 memory list: exits 3 with readable error on stderr.
    • omi --api-base http://127.0.0.1:54321 --json memory list: exits 3 with JSON error payload on stderr and clean stdout.
  • make preflight: all 12 local checks passed.

Fixes #12961

Failure-Class: none

Review in cubic

When httpx.TransportError exhausts retries in OmiClient._request, map the
underlying transport error to ServerError instead of letting it escape to
the unhandled exception handler in main().

- Raise ServerError("Connection failed", detail=...) with original exc as __cause__
- Preserves exit code 3 and JSON error output on stderr for --json
- Add regression tests covering connect, timeout, protocol errors, recovery, and CLI exit codes

Fixes BasedHardware#12961

Signed-off-by: Abhi <108084481+Aj2280@users.noreply.github.com>
Co-authored-by: lrodrig4 <215743880+lrodrig4@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Verified this end to end — a clean, tightly-scoped fix for #12961. Details for the record:

sdks/python-cli/omi_cli/client.py — the new except httpx.TransportError arm in _request() is placed after the _RetryableHttp arm, which is the correct ordering: _RetryableHttp is not a TransportError subclass, so exhausted 5xx/429 retries still route through _error_from_response(), while exhausted transport failures (ConnectError, ReadTimeout, ProtocolError are all TransportError subclasses) now map to ServerError → exit code 3, matching the README's stable contract ("3 server error (5xx, connection failure)"). With tenacity's reraise=True the original exception propagates after stop_after_attempt(4), and raise ... from exc preserves it as __cause__ for debugging. Pre-fix, these escaped to the last-chance except Exception in main() → exit 1 + plain text even with --json, exactly as reported.

sdks/python-cli/tests/test_client_retry.py — the six new tests cover the three transport failure classes exhausting 4 attempts (with retry count asserted), transient-error recovery (a ConnectError followed by 200 returns data), and two end-to-end main() tests asserting exit code 3, empty stdout, and the expected plain/JSON stderr shapes.

Verification run (isolated container, no network/secrets): full python-cli suite passes — 130 passed, 1 skipped, 0 failures (test_openapi_contract.py excluded only because my sandbox lacks the repo-root docs/api-reference/openapi.json it reads). Swapping in the pre-fix client.py makes the new transport tests fail, confirming they capture the regression.

One coordination note: #12996 also addresses #12961 (same transport-to-ServerError mapping, bundled with broader auth-login changes). This PR is the minimal, focused version of the fix. Maintainer decision needed on which one lands — either way, credit to both contributors for chasing the exit-code contract.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@Git-on-my-level Git-on-my-level added positive-signal Automation verified a genuine fix/quality contribution needs-maintainer-review Needs a human maintainer to sign off before merge python labels Sep 8, 2026

@kodjima33 kodjima33 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed not-yet-fixed on main: exhausted-retry handler in _request only catches _RetryableHttp (5xx/429); a persistent httpx.TransportError still bubbles up raw instead of ServerError. Scoped fix + regression tests, confidence 5/5.

@kodjima33
kodjima33 merged commit 42f03c6 into BasedHardware:main Sep 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-maintainer-review Needs a human maintainer to sign off before merge positive-signal Automation verified a genuine fix/quality contribution python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python CLI: exhausted transport retries break exit-code and JSON error contract

3 participants