-
-
Notifications
You must be signed in to change notification settings - Fork 450
Description
- This is NOT a site-related "bugs", e.g. some site blocks me when using curl_cffi,
UNLESS you have verified that the reason is imperfect impersonation. - A code snippet that can reproduce this bug will be provided, even if it's a one-liner.
- Version and environment information will be pasted as below.
Describe the bug
When uploading files using AsyncSession with the multipart parameter, the server receives the request but reports the file field is missing (HTTP 422 error). The exact same upload logic works perfectly with the synchronous Session class.
To Reproduce
import asyncio
from curl_cffi.requests import AsyncSession
from curl_cffi import requests, CurlMime
Test data
test_file_data = b'x' * 30000 # Simulate a 30KB file
Broken: AsyncSession
async def test_async_upload():
session = AsyncSession(impersonate='chrome120', timeout=30, verify=False)
mime = CurlMime()
mime.addpart(
name='file',
content_type='image/jpeg',
filename='test.jpg',
data=test_file_data
)
headers = {
'authorization': 'Bearer test_token',
'referer': 'https://example.com/'
}
try:
# This returns 422: {"detail": [{"type": "missing", "loc": ["body", "file"], "msg": "Field required"}]}
response = await session.post(
'https://api.mubert.xyz/quests/v1/stream/i2m',
multipart=mime,
headers=headers
)
print(f"AsyncSession Status: {response.status_code}")
print(f"Response: {response.text[:200]}")
finally:
mime.close()
await session.close()
Working: Sync Session
def test_sync_upload():
session = requests.Session(impersonate='chrome120', timeout=30, verify=False)
mime = CurlMime()
mime.addpart(
name='file',
content_type='image/jpeg',
filename='test.jpg',
data=test_file_data
)
headers = {
'authorization': 'Bearer test_token',
'referer': 'https://example.com/'
}
try:
# This works! Returns 200 with valid response
response = session.post(
'https://api.mubert.xyz/quests/v1/stream/i2m',
multipart=mime,
headers=headers
)
print(f"Sync Session Status: {response.status_code}")
print(f"Response: {response.text[:200]}")
finally:
mime.close()
session.close()
Run tests
print("Testing AsyncSession:")
asyncio.run(test_async_upload())
print("\nTesting Sync Session:")
test_sync_upload()
Expected behavior
Both AsyncSession and sync Session should successfully upload the file, returning HTTP 200 with the same response body.
Versions
- OS: Windows 11 x64
- curl_cffi version: 0.13.0
Additional context
- Which session are you using? Both tested - AsyncSession fails, sync Session works
- If using async session, which loop implementation are you using? Default asyncio event loop (Python 3.10+)
- If you have tried, does this work with other http clients? Yes, the API itself works fine - sync Session from curl_cffi works, and the original async implementation using asyncio.to_thread() wrapper around sync Session also works
Additional debugging attempts:
- Tried without custom headers - same 422 error
- Tried setting headers via session.headers.update() instead of parameter - same error
- Tried different parameter orders in mime.addpart() - same error
- Verified the mime object is correctly created before sending
- Tried using session.post() directly vs session.request('POST', ...) - same error
Current workaround:
Using asyncio.to_thread() with sync Session works:
async def workaround():
response = await asyncio.to_thread(test_sync_upload)
This suggests the issue is specifically in AsyncSession's handling of the multipart parameter, not in how the mime object is constructed or how curl_cffi interfaces with the server.