docs: document NetworkTransportError and updated HTTP adapter routing#920
Merged
docs: document NetworkTransportError and updated HTTP adapter routing#920
Conversation
Adds NetworkTransportError to the error hierarchy diagram and Python reference, describes the three NETWORK_TRANSPORT_RUNTIME_* ErrorKind values, and extends client-side error handling examples (Python, JS, Java) with a NETWORK_TRANSPORT_ branch. Clarifies in the tool-author guide how the HTTP error adapter now splits exceptions between UpstreamError, NetworkTransportError, and FatalToolError. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
4 tasks
EricGustin
reviewed
Apr 20, 2026
|
|
||
| - **Real HTTP responses** (4xx/5xx) → `UpstreamError` (with `status_code`). 429 becomes `UpstreamRateLimitError`. | ||
| - **No response received** (connect/read timeouts, DNS failures, connection errors, TLS handshake failures, decoding errors, redirect-loop exhaustion) → `NetworkTransportError` with `status_code=None`. Classified by `ErrorKind` as `NETWORK_TRANSPORT_RUNTIME_TIMEOUT`, `NETWORK_TRANSPORT_RUNTIME_UNREACHABLE`, or `NETWORK_TRANSPORT_RUNTIME_UNMAPPED`. | ||
| - **Client construction bugs** (`InvalidURL`, `UnsupportedProtocol`, `MissingSchema`, `InvalidSchema`, `InvalidHeader`, `InvalidProxyURL`, `URLRequired`, `SSLError`) → `FatalToolError`. These indicate a tool-authoring mistake or local TLS configuration issue and are not retryable. |
Member
There was a problem hiding this comment.
I think this needs distinction between tool-authored URLs, schemas, headers, etc. and caller-provided values. In other words, is it really a tool-authoring mistake if the caller provides a invalid URL?
EricGustin
approved these changes
Apr 20, 2026
jottakka
added a commit
to ArcadeAI/arcade-mcp
that referenced
this pull request
Apr 20, 2026
## Summary Routes HTTP adapter exceptions to the right error class instead of shoe-horning everything into `UpstreamError`. Addresses Eric's earlier feedback that several exceptions this PR was wrapping as `UpstreamError` didn't satisfy the "something happened with the upstream" claim (local pool exhaustion, client-side request construction, local TLS failures). ### Scope - `UpstreamError` (unchanged) — upstream responded with an HTTP status code. - **`NetworkTransportError`** (new sibling in `arcade-core`) — no complete response was received. `status_code=None`. Three kinds: `NETWORK_TRANSPORT_RUNTIME_TIMEOUT`, `_UNREACHABLE`, `_UNMAPPED`. - **`FatalToolError`** (existing) — client construction bugs (`InvalidURL`, `UnsupportedProtocol`, `MissingSchema`, `InvalidHeader`, `LocalProtocolError`, …) and local TLS/cert config failures. Never retried. --- ## Before / After (per Eric's request) Shows the error payload a tool produces for each exception, before this PR vs. after. "Before" = current `main` (exceptions without real HTTP responses fall through to the generic `@tool` `FatalToolError` catch-all with `message=str(exc)`). ### No-response transport failures | Exception | Before — class / message / kind | After — class / message / kind | |---|---|---| | `httpx.PoolTimeout` | `FatalToolError` — `str(exc)` leaks raw detail — `TOOL_RUNTIME_FATAL`, not retryable | `NetworkTransportError` — `"HTTP request timed out before a complete response was received."` — `NETWORK_TRANSPORT_RUNTIME_TIMEOUT`, **retryable** | | `httpx.ConnectTimeout` | same as above | same as PoolTimeout — `TIMEOUT`, retryable | | `httpx.ConnectError` (refused / DNS) | `FatalToolError` — `str(exc)` | `NetworkTransportError` — `"HTTP request failed before reaching the upstream service."` — `UNREACHABLE`, retryable | | `httpx.RemoteProtocolError` (upstream sent bad HTTP) | `FatalToolError` — `str(exc)` | `NetworkTransportError` — same message as ConnectError — `UNREACHABLE`, retryable | | `httpx.DecodingError` | `FatalToolError` — `str(exc)` | `NetworkTransportError` — `"HTTP response from upstream could not be decoded."` — `UNMAPPED`, retryable | | `httpx.TooManyRedirects` | `FatalToolError` — `str(exc)` | `NetworkTransportError` — `"HTTP redirect limit exceeded before a final response was received."` — `UNMAPPED`, **not** retryable | ### Client construction / local env bugs | Exception | Before | After | |---|---|---| | `httpx.UnsupportedProtocol`, `httpx.InvalidURL`, `httpx.LocalProtocolError` | `FatalToolError` with `message=str(exc)` (may leak scheme / URL content) | `FatalToolError` — `"Tool constructed an invalid HTTP request — likely a tool-authoring bug."` — `TOOL_RUNTIME_FATAL`, not retryable | | `requests.MissingSchema`, `InvalidURL`, `InvalidHeader`, `InvalidSchema`, `InvalidProxyURL`, `URLRequired` | same as above | same as above | | `requests.SSLError` | `FatalToolError` — `str(exc)` often contains raw cert chain detail | `FatalToolError` — `"TLS handshake failed — likely a local certificate or trust configuration issue."` — `TOOL_RUNTIME_FATAL`, not retryable | ### Real HTTP response errors (UNCHANGED — same behavior) | Exception | Class | Message | Kind | Retryable | |---|---|---|---|---| | `httpx.HTTPStatusError` 404 | `UpstreamError` | `"Upstream HTTP request failed (Not Found, client error)."` | `UPSTREAM_RUNTIME_NOT_FOUND` | No | | `httpx.HTTPStatusError` 429 (w/ Retry-After: 60) | `UpstreamRateLimitError` | `"Upstream HTTP request failed (Too Many Requests, client error). Retry after 60 second(s)."` | `UPSTREAM_RUNTIME_RATE_LIMIT` | Yes | | `httpx.HTTPStatusError` 500 | `UpstreamError` | `"Upstream HTTP request failed (Internal Server Error, server error)."` | `UPSTREAM_RUNTIME_SERVER_ERROR` | Yes | ### What's no longer in the message - Raw exception `str(exc)` output (which frequently includes the full URL with query-string tokens, connection pool details, or cert chains) is **no longer the agent-facing `message`**. It's preserved in `developer_message` for server-side diagnostics. - The misleading "Upstream HTTP…" prefix is gone from network-transport and construction-bug messages. Those messages now honestly describe what happened on the tool side. - For 429s without a `Retry-After` header, we still show "Retry after N seconds." (pre-existing behavior; see follow-up notes). --- ## Companion PRs - [#823](#823) — introduces `NetworkTransportError` in `arcade-core` - [ArcadeAI/monorepo#911](ArcadeAI/monorepo#911) — adds the 3 `ErrorKind` constants to the Go engine and Datadog dashboards - [ArcadeAI/docs#920](ArcadeAI/docs#920) — documents the new hierarchy and adapter routing ## Follow-ups (out of scope for this PR) A short investigation surfaced several pre-existing issues that are worth fixing separately. A full list is in `NETWORK_TRANSPORT_ERROR_FOLLOWUPS.md` (shared offline). Summary: 1. `requests.HTTPError` with `response is None` returns `None` from the adapter; should fall through to the `NetworkTransportError(UNMAPPED)` fallback instead of becoming a generic `FatalToolError`. 2. `developer_message` can leak URL query strings (and therefore tokens) since it stores raw `str(exc)`. 3. `_sanitize_uri` does not strip userinfo (credentials in URL path). 4. `_parse_retry_ms` misinterprets epoch-style `x-ratelimit-reset` headers. 5. 429 responses without `Retry-After` synthesize a fabricated "Retry after 1 second(s)." suffix. 6. `UPSTREAM_RUNTIME_VALIDATION_ERROR` is defined but never emitted. 7. `UpstreamError` silently accepts out-of-range status codes. 8. `requests.HTTPError` branch re-extracts `request_url` / `request_method` inconsistently (dead work). ## Test plan - [x] Existing `libs/tests/sdk/test_httpx_adapter.py` + `test_graphql_adapter.py` updated; every no-response / construction-bug test asserts the new class + kind + `can_retry`. - [x] Full test suite passes locally. - [x] mypy clean on `arcade-core`, `arcade-tdk`, `arcade-mcp-server`. - [x] Smoke-tested 21 exception routing cases end-to-end against real httpx / requests exceptions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes core error classification and retryability for `httpx`/`requests`/GraphQL transport failures, which can affect tool retry behavior and telemetry. Risk is mitigated by extensive new/updated tests covering the new mappings and privacy expectations. > > **Overview** > **Improves error adapter behavior to be more semantically correct and privacy-safe.** The HTTP adapter now distinguishes real HTTP responses (`UpstreamError`/`UpstreamRateLimitError`) from no-response failures (`NetworkTransportError` with `ErrorKind` + retryability) and from client construction/local TLS issues (`FatalToolError`). > > **Reduces sensitive data exposure in agent-facing messages.** Status-based errors now emit standardized messages derived from status phrase/class, while preserving raw exception detail in `developer_message`; Google/Microsoft/Slack fallback paths similarly switch to `unhandled <ExceptionType>` messages and move `str(exc)` into `developer_message`. GraphQL transport connection/protocol errors are reclassified from `UpstreamError` (502) to `NetworkTransportError`, and transport/server messages are standardized. > > Bumps `arcade-tdk` version to `3.8.0` and expands/updates the SDK test suite to assert new classes, `kind`, `can_retry`, request metadata extraction, and privacy behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 1041cb1. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ting - Active voice for HTTP adapter sentence (Google.Passive) - Replace e.g. with 'for example' (Google.Latin) - Remove spaces around em dashes (Google.EmDash) - Clarify FatalToolError note: caller-provided values also routed here if unvalidated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Documents the new
NetworkTransportErrorclass and updated HTTP adapter routing landing in:What's new
NetworkTransportError— sibling ofUpstreamErrorunderToolExecutionError, for failures where no complete HTTP response was received (timeouts, connection errors, decoding, redirect exhaustion). Hasstatus_code=None.ErrorKindvalues:NETWORK_TRANSPORT_RUNTIME_TIMEOUT,_UNREACHABLE,_UNMAPPED.InvalidURL,UnsupportedProtocol,MissingSchema,SSLError, etc.) toFatalToolErrorinstead ofUpstreamError.Files changed
app/_components/error-hierarchy.tsxNetworkTransportErrorrow to the ASCII hierarchy diagramapp/en/references/mcp/python/errors/page.mdxNetworkTransportErrorentry and list the 3 newErrorKindvaluesapp/en/guides/create-tools/error-handling/useful-tool-errors/page.mdxUpstreamErrorvsNetworkTransportErrorvsFatalToolErrorfor HTTP adapterapp/en/guides/tool-calling/error-handling/page.mdxNETWORK_TRANSPORT_branchTest plan
ErrorKindvalues are correctly linked🤖 Generated with Claude Code
Note
Low Risk
Low risk doc-only changes updating error taxonomy and example branches; main risk is minor confusion if naming drifts from released client/server versions.
Overview
Documents the new
NetworkTransportErroras a sibling toUpstreamErrorfor HTTP failures where no response is received, including itsstatus_code=Nonebehavior and the newErrorKindclassifications.Updates the error-hierarchy diagram and the HTTP adapter routing guidance to distinguish real HTTP responses (
UpstreamError/UpstreamRateLimitError) vs transport failures (NetworkTransportError) vs client construction bugs (FatalToolError), and extends Python/JS/Java client error-handling examples with aNETWORK_TRANSPORT_branch.Reviewed by Cursor Bugbot for commit b6e8614. Bugbot is set up for automated code reviews on this repo. Configure here.