Tutorials
Ren Okabe8 min read3 views

Invalid http_client argument: anthropic 1.x Runs on httpx2, and Two Cases the Guard Misses (2026)

TypeError: Invalid http_client argument means the anthropic Python SDK moved to httpx2 in 1.0.0. The rename is one import. The parts nobody mentions are that the guard printing that message only exists from 1.4.0, and that it cannot see your respx or OpenTelemetry layer at all (2026).

A dark technical diagram: a muted grey data stream of evenly spaced nodes runs left to right and passes through a rectangular inspection frame clamped around it, while a brighter electric blue stream below runs the full width completely clear of that frame, never touching it.
A dark technical diagram: a muted grey data stream of evenly spaced nodes runs left to right and passes through a rectangular inspection frame clamped around it, while a brighter electric blue stream below runs the full width completely clear of that frame, never touching it.
On this page

Quick Answer (2026)

text
TypeError: Invalid `http_client` argument; `httpx.Client` is from the `httpx`
package, but this SDK uses `httpx2`. Use `httpx2.Client` instead.

Anthropic The anthropic Python SDK shipped 1.0.0 on 20 August 2026, and its one breaking change was moving the HTTP layer from httpx to httpx2. Any httpx object you hand the client is now the wrong type. The fix is one import:

Python import httpx2 as httpx, or switch the individual references.

That is the loud half, and it is the only half the error message covers. Two things about this migration do not produce that error, or any error:

  1. The guard that prints that message did not exist until 1.4.0 (4 September 2026). On 1.0.0 through 1.3.0 the same mistake fails deep inside a request instead.
  2. The guard cannot see your test suite. respx, pytest-httpx, vcrpy, Sentry and OpenTelemetry patch the httpx package, which the SDK no longer imports. They keep passing. They just stop seeing the SDK's traffic.

The second one is the expensive one, because nothing turns red. Your mocks stop intercepting and your tests start making real API calls.

Verified against anthropic 1.5.0, read at source, in September 2026.

Why this broke on a day you changed nothing

PyPI Version 1.0.0 landed on 20 August 2026. The changelog lists exactly one breaking change:

Release notes, verbatim: "client: upgrade to httpx2 and some minor breaking changes. See MIGRATION.md for details"

Nothing in your code had to change to be affected. An unpinned requirement, a rebuilt container, a fresh lockfile, or a CI runner that does not cache wheels is enough. Confirm which side of the line you are on:

bash
python -c "import importlib.metadata as m; print(m.version('anthropic'))"

Anything starting 1. is the new major. The dependency swap is visible in the package metadata itself. Release 0.125.0, the last of the 0.x line, declared a requirement on httpx in the 0.25 up to 1.0 range. Release 1.5.0 declares one on httpx2, from 2.0 up to 3.0.

Pydantic httpx2 is an API-compatible fork of httpx maintained by the Pydantic team. Same classes, same behaviour. This is not an Anthropic-specific decision: the OpenAI Python SDK made the same move in its own 3.0 release.

Which of your code is actually affected

Most code is not. If you only ever pass plain values, there is nothing to do:

python
from anthropic import Anthropic

# Untouched. Plain values, no httpx objects anywhere.
client = Anthropic(timeout=30.0, max_retries=3)

Three argument names accept httpx objects and are therefore guarded. Read at source in _base_client.py at tag v1.5.0, the validator _reject_httpx_object is called at six sites covering exactly timeout, transport and http_client, on both the sync and async clients. That is the whole guarded surface.

python
# Before, on 0.x
import httpx
from anthropic import Anthropic, DefaultHttpxClient

client = Anthropic(
    timeout=httpx.Timeout(60.0, connect=5.0),
    http_client=DefaultHttpxClient(
        transport=httpx.HTTPTransport(local_address="0.0.0.0"),
    ),
)

# After, on 1.x
import httpx2
from anthropic import Anthropic, DefaultHttpxClient

client = Anthropic(
    timeout=httpx2.Timeout(60.0, connect=5.0),
    http_client=DefaultHttpxClient(
        transport=httpx2.HTTPTransport(local_address="0.0.0.0"),
    ),
)

Note what did not need editing there. DefaultHttpxClient is an SDK re-export and already points at httpx2. The same is true of anthropic.Timeout, anthropic.DefaultAsyncHttpxClient and anthropic.DefaultAioHttpClient. If you built your custom client entirely out of SDK re-exports, you are already done.

Two top-level names were removed rather than repointed: anthropic.Transport and anthropic.ProxiesTypes. Use httpx2.BaseTransport, httpx2.AsyncBaseTransport and httpx2.Proxy.

The version boundary nobody mentions

The error message in the Quick Answer is helpful, specific, and younger than the release that made it necessary.

GitHub The 1.4.0 changelog, released 4 September 2026, lists it as a bug fix: "client: raise a clear error when an httpx object is passed instead of an httpx2 one". Checking _base_client.py across four tags, _reject_httpx_object appears 0 times at v1.0.0, 0 times at v1.3.0, and 7 times at v1.4.0 and v1.5.0.

So there is a 15 day window, 20 August to 4 September 2026, in which anthropic 1.x was on PyPI with no guard at all. If you are pinned anywhere in 1.0 to 1.3, you do not get the clear message. You get whatever failure the mismatched object produces once a request is already in flight, which the guard's own docstring describes:

python
def _reject_httpx_object(name: str, value: object) -> None:
    """`httpx` objects don't work with `httpx2`, and would otherwise fail deep inside a request."""

If you are debugging a confusing connection-layer traceback on 1.0 to 1.3, upgrading to 1.4.0 or newer is a diagnostic step, not just a version bump. It converts a deep failure into a named argument.

The case no guard can catch

Here is the part that matters most if you have a test suite.

A guard on timeout, transport and http_client works by inspecting what you hand the SDK. Mocking and tracing libraries never hand the SDK anything. They patch the httpx package itself, and the SDK no longer imports it. There is nothing for a guard to inspect.

Sentry Anthropic's own migration guide is direct about this:

MIGRATION.md, verbatim: "Libraries that observe or stub HTTP traffic by patching httpx ... patch the httpx package, which the SDK no longer uses. These libraries can silently fail, making it difficult to identify failure points."

The named list is respx, pytest-httpx, vcrpy, OpenTelemetry's HTTPXClientInstrumentor and Sentry's httpx integration. Two different bad outcomes follow, depending on which one you use:

OpenTelemetry Instrumentation stops producing spans for Claude calls. Your traces are not wrong, they are incomplete, and the missing segment is the one you most wanted to see.

pytest Mocking is worse. A mock that no longer intercepts does not fail the request, it lets it through. A test you believed was hermetic now reaches the real API. In CI with no key that surfaces as an authentication error you will probably misread as a secrets problem. With a key present, it passes, bills you, and quietly becomes a live integration test.

The fix is one call, made once, before anything imports httpx:

python
import httpx2

httpx2.alias_httpx()  # `import httpx` now resolves to httpx2

Two constraints on it, both worth respecting. It raises RuntimeError if httpx has already been imported, so it belongs at the very top of your entry point. And it is process-wide, so a library should never call it on a user's behalf. This is an application-level decision.

Under pytest, an early plugin runs it before respx and your test modules are imported:

python
# tests/_alias_httpx.py
import httpx2

httpx2.alias_httpx()
toml
# pyproject.toml
[tool.pytest.ini_options]
addopts = "-p tests._alias_httpx"
pythonpath = ["."]

If you are building the harness rather than repairing one, our pytest and LLM-judge harness avoids the problem by not stubbing at the HTTP layer at all.

A second silent case: Python 3.9

The minimum Python version moved from 3.9 to 3.10 in the same release, and this interacts badly with an unpinned upgrade.

On Python 3.9, pip install --upgrade anthropic does not fail. It resolves to 0.125.0, the newest release that still declares requires_python of 3.9 or greater. You get a successful upgrade, no warning, and none of 1.x. Anyone reading your lockfile sees anthropic at a version they may assume is current.

Check the interpreter before concluding anything about the SDK version:

bash
python -c "import sys, importlib.metadata as m; print(sys.version_info[:2], m.version('anthropic'))"

(3, 9, ...) paired with a 0. version is this case, not a resolver bug.

Verify the migration actually took

Three checks, in increasing order of what they prove:

python
# 1. You are on 1.x and it imports.
import importlib.metadata as m
print(m.version("anthropic"))

# 2. No httpx objects reach the client. Raises on 1.4.0+, passes silently if clean.
import httpx2
from anthropic import Anthropic
Anthropic(timeout=httpx2.Timeout(30.0), api_key="not-used-for-construction")

# 3. Your interception layer still sees the SDK. This is the one that matters.
import httpx
print(httpx.Client is httpx2.Client)  # True only after alias_httpx()

Check 3 is the one to add to your test suite. Checks 1 and 2 tell you the SDK is healthy. Only check 3 tells you your mocks are still attached to it, and that is the failure with no error message.

If you also run MCP clients, the same httpx2 swap landed there in MCP Python SDK 2.0, with its own silently dropped read timeout.

R

Written by

Ren Okabe

Ren builds and breaks agent tooling, then writes down the parts the documentation assumes you already know.

Frequently asked questions

What does Invalid http_client argument mean in the anthropic SDK?

It means you passed an object from the old httpx package to a client that now runs on httpx2. The anthropic Python SDK moved its HTTP layer to httpx2 in version 1.0.0, released 20 August 2026, and that was the release's only breaking change. The fix is to construct the object from httpx2 instead, or to alias the import with 'import httpx2 as httpx'.

Which arguments actually reject httpx objects?

Exactly three: timeout, transport and http_client. Read at source in _base_client.py at tag v1.5.0, the validator _reject_httpx_object is called at six sites covering those three names on both the sync and the async client. If you only ever pass plain values such as timeout=30.0, nothing in your code is affected.

Why am I not seeing that error message even though I passed an httpx object?

Because the guard that prints it was added in 1.4.0, released 4 September 2026, as a bug fix described as raising a clear error when an httpx object is passed instead of an httpx2 one. Checking the source across tags, the validator appears zero times at v1.0.0 and v1.3.0 and seven times at v1.4.0 and v1.5.0. On 1.0 through 1.3 the same mistake fails deep inside a request instead, which is much harder to read.

Will this break my tests even if I never pass an httpx object?

Yes, and silently. respx, pytest-httpx, vcrpy, Sentry and OpenTelemetry all work by patching the httpx package, which the SDK no longer imports, so they keep passing while no longer seeing the SDK's traffic. A mock that stops intercepting does not fail the request, it lets it through, so a test you believed was hermetic starts calling the real API. Call httpx2.alias_httpx() once before anything imports httpx.

Do I need to change DefaultHttpxClient or anthropic.Timeout?

No. Those SDK re-exports already point at httpx2 and keep working unchanged, as do DefaultAsyncHttpxClient and DefaultAioHttpClient. Two names were removed rather than repointed: anthropic.Transport and anthropic.ProxiesTypes. Use httpx2.BaseTransport, httpx2.AsyncBaseTransport and httpx2.Proxy instead.

I upgraded on Python 3.9 and still have a 0.x version. Why?

Version 1.0.0 also raised the minimum Python from 3.9 to 3.10. On Python 3.9 the resolver cannot take any 1.x release, so pip install --upgrade anthropic quietly settles on 0.125.0, the newest release that still supports 3.9. It reports success and you get none of 1.x. Print sys.version_info alongside the package version before concluding anything about the SDK.