Skip to content

fix(api): clean dynamic API HTTP and connection errors#45

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

fix(api): clean dynamic API HTTP and connection errors#45
arseniy-pplx wants to merge 1 commit into
TangleML:masterfrom
arseniy-pplx:transfer/clean-dynamic-api-errors

Conversation

@arseniy-pplx

Copy link
Copy Markdown

Summary

Dynamic tangle api <group> <op> calls (and the OpenAPI schema refresh/fetch path) now fail with a concise, one-line, credential-safe message and a non-zero exit code instead of a raw traceback or a truncated HTTP status exit code.

  • HTTP status errors render as HTTP <status> <reason> for <METHOD> <url> with a whitespace-normalized, length-bounded slice of the backend body.
  • Connection, timeout, proxy, and TLS errors render an actionable reason (e.g. connection failed: [Errno 111] Connection refused, read timed out, TLS error: ...).
  • URL sanitization strips user:pass@ userinfo and redacts credential query params, Authorization, and presigned/SAS-URL signatures (AWS SigV4 / GCS / Azure). IPv6 literals are re-bracketed so host and port survive redaction (https://<redacted>@[2001:db8::1]:8443/...).
  • Free-form exception text is scrubbed for embedded URLs and bare user:pass@host userinfo before display, so a crafted or third-party proxy/connect error string cannot leak credentials — while benign diagnostics (errno, TLS reason) are preserved.
  • Exit codes are a non-zero string exit (1) rather than the raw HTTP status, which was previously truncated to 8 bits (404 → 148, 512 → 0 false success).
  • Schema refresh/fetch failures reuse the same formatting and keep omitting the /openapi.json response body, so an auth failure cannot reflect the credentials just sent.

The original exception is preserved as the cause (raise ... from exc) for Python callers; programmatic clients still receive raw httpx exceptions. Changes are confined to api_transport.py and api_cli.py; the existing actionable cache-missing behavior and API command exit semantics are unchanged.

Context

Dynamic tangle api calls previously surfaced backend failures either as a raw traceback or as an HTTP status pushed through the process exit code, where the value was truncated to 8 bits (404 → 148, 512 → 0 false success). This renders every failure as one concise, credential-safe line with a correct non-zero exit code, without changing the success path or the actionable cache-missing behavior.

Testing

  • uv run pytest tests/test_api_transport.py — URL sanitization (userinfo, credential/query params, presigned signatures, IPv6 re-bracketing with userinfo+port), one-line status errors, bounded/normalized detail, detail omission, actionable request-error classification, embedded-secret scrubbing for proxy/connect exception text, and benign errno/TLS diagnostics preserved
  • uv run pytest tests/test_api_cli.py — passes except one pre-existing failure (test_official_static_command_without_schema_fails_with_actionable_error) that reproduces identically on the base commit and is unrelated to this change
  • uvx ruff check on the touched files
  • uv lock --check; uv build
  • git diff --check
  • dynamic CLI smoke against a refused port with URL credentials renders Failed to reach GET http://<redacted>@127.0.0.1:1/api/pipeline_runs/: connection failed: [Errno 111] Connection refused, exit 1

Dynamic `tangle api <group> <op>` calls now fail with a concise, one-line,
credential-safe message and a non-zero exit instead of a raw traceback or a
truncated HTTP status exit code.

- HTTP status errors report `HTTP <status> <reason> for <METHOD> <url>` with a
  whitespace-normalized, length-bounded slice of the backend body.
- Connection, timeout, proxy, and TLS errors report an actionable reason.
- URLs are sanitized: userinfo, credential query params, Authorization, and
  presigned/SAS-URL signatures are redacted. IPv6 literals are re-bracketed so
  host and port survive redaction (`[2001:db8::1]:8443`).
- Free-form exception text is scrubbed for embedded URLs and `user:pass@host`
  userinfo before display, so a crafted or third-party proxy/connect error
  cannot leak credentials while errno/TLS diagnostics are preserved.
- Exit codes are now a non-zero string exit (1) rather than the HTTP status,
  which was truncated to 8 bits (404 -> 148, 512 -> 0).
- Schema refresh/fetch failures reuse the same formatting and keep omitting the
  response body so an auth failure cannot reflect sent credentials.

The original exception is preserved as the cause for Python callers, and
programmatic clients still receive raw httpx exceptions.
@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