Skip to content

Render transport failures as one clean CLI line#46

Open
arseniy-pplx wants to merge 1 commit into
TangleML:masterfrom
arseniy-pplx:transfer/clean-transport-errors
Open

Render transport failures as one clean CLI line#46
arseniy-pplx wants to merge 1 commit into
TangleML:masterfrom
arseniy-pplx:transfer/clean-transport-errors

Conversation

@arseniy-pplx

Copy link
Copy Markdown

Summary

Connection, timeout, TLS, proxy, and mid-response failures from the static requests client previously bubbled up as a full Python traceback. Besides being noisy, the raw exception text can embed proxy credentials (userinfo) or signed-URL query secrets, so it was also a disclosure risk. This change turns those no-response transport failures into a single, credential-safe line.

  • api_transport.py adds three reusable helpers: sanitize_destination() reduces a URL to scheme://host[:port]/path (dropping userinfo, query, and fragment; IPv6 literals re-bracketed so [2001:db8::1]:8443 stays a valid origin); transport_error_reason() maps a requests transport exception to a short phrase (e.g. TLS verification failed, connection timed out, connection closed mid-response), most-specific type first; format_transport_error() composes Could not reach Tangle API (<METHOD> <destination>): <reason> without echoing raw exception text.
  • client.py performs the conversion at a single public boundary, _send_request(), which wraps _make_request(). A no-response RequestException that survives every layer becomes a TangleApiTransportError (a requests.exceptions.RequestException subclass) carrying the formatted line, with the original chained as __cause__. _make_request() and the rate-limit/redirect layers beneath it re-raise the original requests exception subtypes untouched, so a lower retry layer can still classify connect/timeout failures. Errors that carry an HTTP response are re-raised unchanged, so status and retry handling are untouched.
  • component_inspector.py routes its direct request through _send_request() for the same clean-error treatment.
  • cli.py prints the one-line message to stderr and exits nonzero at a run() boundary. It only intercepts transport failures (no response); HTTP status errors and ordinary programmer errors propagate as before, and it also formats a raw transport exception that bypassed the client.

Context

Redaction, method, and sanitized destination are applied consistently across every static-client command (pipeline-runs, artifacts, components, pipelines, published-components, secrets) because they all route through the same _send_request() boundary.

Testing

  • uv run pytest tests/test_transport_errors.py tests/test_packaging.py covering URL sanitization (userinfo, query including signed-URL params, fragment), IPv6 re-bracketing, reason classification (SSL/proxy/connect-timeout/read-timeout/timeout/chunked/connection/generic), the formatter never echoing raw secret-bearing text, the inner redirect strategy and _make_request re-raising the raw requests subtype unchanged, _send_request converting to TangleApiTransportError with __cause__ preserved, HTTP-response errors re-raised unchanged (programmer errors not swallowed), a genuinely refused localhost port yielding a clean domain error, and the CLI rendering one stderr line with a nonzero exit
  • uvx ruff check on the touched files
  • uv lock --check; wheel build
  • git diff --check

Connection, timeout, TLS, proxy, and mid-response failures from the
static requests client previously surfaced as a full traceback that
could echo proxy credentials or signed-URL query secrets. Classify
these no-response transport errors into a short reason phrase and
format a single, credential-safe line that includes the method and a
sanitized destination (userinfo, query, and fragment stripped).

Native requests exceptions stay raw through the retry and redirect
layers so those layers can still classify and retry connect/timeout
failures; conversion happens only at the public _send_request boundary,
which wraps a surviving no-response RequestException as a
TangleApiTransportError (a requests.exceptions.RequestException subclass)
with the original chained as __cause__. The CLI prints one line to
stderr and exits nonzero. Errors that carry an HTTP response and
programmer errors are never intercepted, so status and retry handling
stay intact.
@arseniy-pplx
arseniy-pplx marked this pull request as ready for review July 20, 2026 18:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant