Fix confusing error message for invalid data parameter

When AsyncClient receives invalid data like data=[{"a": "b"}] (list of dicts),
it was raising "Attempted to send a sync request with an AsyncClient instance"
which is misleading. The actual issue is invalid data format.

This fix adds early validation in encode_request() to check if data is a
list/tuple containing non-bytes objects, and raises a clear TypeError with
helpful guidance (use json= or data={...} instead).

Fixes #3471
This commit is contained in:
Varun Chawla 2026-02-07 17:48:26 -08:00
parent b5addb64f0
commit a5e4db62be
No known key found for this signature in database
2 changed files with 36 additions and 0 deletions

View File

@ -202,6 +202,21 @@ def encode_request(
# However for compat with requests, we *do* still support
# `data=<bytes...>` usages. We deal with that case here, treating it
# as if `content=<...>` had been supplied instead.
# Validate that data is bytes-like or an iterable of bytes, not other types
if isinstance(data, (list, tuple)):
# Check if it's a list/tuple of bytes
try:
for item in data:
if not isinstance(item, (bytes, bytearray, memoryview)):
raise TypeError(
f"Expected bytes-like object in 'data' sequence, got {type(item).__name__}. "
f"Use 'json=' for JSON data or 'data={{...}}' for form data."
)
except TypeError:
# Re-raise our custom error, not the iteration error
raise
message = "Use 'content=<...>' to upload raw bytes/text content."
warnings.warn(message, DeprecationWarning, stacklevel=2)
return encode_content(data)

View File

@ -364,6 +364,27 @@ def test_invalid_argument():
httpx.Request(method, url, content={"a": "b"}) # type: ignore
def test_invalid_data_list_of_dicts():
"""Test that passing a list of dicts to data= produces a clear error message.
This was previously raising a confusing 'Attempted to send a sync request
with an AsyncClient instance' error with AsyncClient. Now it should give
a clear error for both sync and async clients.
"""
with pytest.raises(TypeError, match="Expected bytes-like object.*got dict"):
httpx.Request(method, url, data=[{"a": "b"}]) # type: ignore
@pytest.mark.anyio
async def test_invalid_data_list_of_dicts_async():
"""Test that AsyncClient produces clear error for invalid data parameter.
Regression test for issue #3471.
"""
with pytest.raises(TypeError, match="Expected bytes-like object.*got dict"):
httpx.Request(method, url, data=[{"a": "b"}]) # type: ignore
@pytest.mark.anyio
async def test_multipart_multiple_files_single_input_content():
files = [