Skip to content

bug: multipart/formdata boundary is not set in header #3522

@Anton-Shutik

Description

@Anton-Shutik
python==3.12
httpx==0.28.1

I'm having an issue while sending data with "Content-Type": "multipart/form-data", while sending same data with "Content-Type": "multipart/form-data; boundary=WebKitFormBoundary7MA4YWxkTrZu0gW" works fine.

The boundary is added to the multipart stream anyways (if not provided in the Content-type header - it is autogenerated), but it also should be appended to the header itself, so server knows how to parse multipart stream.

Take a look at full example:

from curlify2 import Curlify

headers = {
    "Authorization": f"Bearer {self.api_token}",
    "Content-Type": "multipart/form-data",
    # "Content-Type": "multipart/form-data; boundary=WebKitFormBoundary7MA4YWxkTrZu0gW",
}

# Prepare form data as tuples for multipart/form-data
files = [("image_url", (None, url)) for url in photo_urls]

async with httpx.AsyncClient() as client:
    try:
        req = await client.post(endpoint, headers=headers, files=files, )
        response = Curlify(req.request)
        print("#" * 20, "Start", "#" * 20)
        print(response.to_curl())
        print("#" * 20, "End", "#" * 20)

Prints this:

# #################### Start ####################
# curl -X POST -H "host: <3rdparty>" -H "accept: */*" -H "accept-encoding: gzip, deflate, zstd" -H "connection: keep-alive" -H "user-agent: python-httpx/0.28.1" -H "authorization: Bearer <token>" -H "content-type: multipart/form-data" -H "content-length: 186" -d '--d21e39e0c438fedf66c71fa2514f0ac5
# Content-Disposition: form-data; name="image_url"
# 
# https://<storage>/logo_3.webp
# --d21e39e0c438fedf66c71fa2514f0ac5--
# ' https://<3rdparty>/endpoint
# #################### End ####################
# Fails with: fastapi.exceptions.HTTPException: 400: error: {"error":{"code":400,"message":"missing form parameter 'image_url'"}}

Now, with the boundary (just switched them in headers dict) explicitly provided in the header. Works fine

# #################### Start ####################
# curl -X POST -H "host: <3rd party>" -H "accept: */*" -H "accept-encoding: gzip, deflate, zstd" -H "connection: keep-alive" -H "user-agent: python-httpx/0.28.1" -H "authorization: Bearer <token>" -H "content-type: multipart/form-data; boundary=WebKitFormBoundary7MA4YWxkTrZu0gW" -H "content-length: 188" -d '--WebKitFormBoundary7MA4YWxkTrZu0gW
# Content-Disposition: form-data; name="image_url"
# 
# https://<storage>/logo_3.webp
# --WebKitFormBoundary7MA4YWxkTrZu0gW--
# ' https://<3rdparty>/endpoints
# #################### End ####################

Notice, that in the second case boundary is present in the header, while in the first isn't. I think this is a bug

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions