Summary
At current main (73c4dbead99be6515fa25fcd91e348ac30f5c22e), a schema-discovered BentoML client sending two URL-backed values for one multipart list[Path] field sends only the final URL value. I reproduced the same loss through the HTTPX sync/async clients and the separate aiohttp/RemoteProxy client.
A direct multipart request with two same-name parts reaches the service as two values, and local Path uploads through the client also preserve both.
Service under test
from pathlib import Path
import bentoml
@bentoml.service(metrics={"enabled": False})
class MultipartService:
@bentoml.api
def echo(self, files: list[Path]) -> list[str]:
return [path.read_text() for path in files]
I used two distinct URL-backed fixtures whose contents were ALPHA and BRAVO. The clients were initialized in remote-schema mode (service is None; schema loaded from /schema.json; endpoint input_spec is None). I replaced only URL retrieval with an in-memory URL-to-UploadFile mapping so the test was deterministic and made no network request; client serialization, ASGI parsing, and service dispatch ran from the pinned source unchanged.
Results
DIRECT_URL_CONTROL ["ALPHA", "BRAVO"]
LOCAL_FILE_CLIENT_CONTROL ["ALPHA", "BRAVO"]
HTTPX Sync remote client ["BRAVO"]
HTTPX Async remote client ["BRAVO"]
proxy2 remote client ["BRAVO"]
The direct control submitted:
files = [
("files", (None, "https://files.example.invalid/a.txt")),
("files", (None, "https://files.example.invalid/b.txt")),
]
For the client cases, the equivalent call was:
client.echo(files=[url_a, url_b])
Expected
Each URL in a multipart list field should produce one same-name part, so the service should receive both values in order, matching the direct-request and local-file controls.
Source-level cause
Both client implementations iterate the list but assign URL strings through data[name] = file. Because data is a mapping, each URL overwrites the previous value. Concrete local files instead use the repeated-entry files list. On the server side, MultipartSerde.parse_request() uses form.getlist(k), so repeated values are preserved if the client actually sends them.
Affected implementations:
src/_bentoml_impl/client/http.py (_build_multipart used by HTTPX sync/async clients)
src/_bentoml_impl/client/proxy2.py (duplicated aiohttp/RemoteProxy multipart builder)
This report is limited to remote-schema URL-backed list inputs. Local file uploads and direct repeated multipart requests are unaffected. I am not making a security claim, and this does not appear to be an OpenAPI-schema defect.
I searched open and closed issues/PRs for the builder, URL-backed multipart lists, and last-value overwrite behavior and did not find a direct match. I have a focused service-level regression covering the direct control and all three client paths. Would a narrow fix and regression test be welcome?
Summary
At current
main(73c4dbead99be6515fa25fcd91e348ac30f5c22e), a schema-discovered BentoML client sending two URL-backed values for one multipartlist[Path]field sends only the final URL value. I reproduced the same loss through the HTTPX sync/async clients and the separate aiohttp/RemoteProxy client.A direct multipart request with two same-name parts reaches the service as two values, and local
Pathuploads through the client also preserve both.Service under test
I used two distinct URL-backed fixtures whose contents were
ALPHAandBRAVO. The clients were initialized in remote-schema mode (service is None; schema loaded from/schema.json; endpointinput_spec is None). I replaced only URL retrieval with an in-memory URL-to-UploadFilemapping so the test was deterministic and made no network request; client serialization, ASGI parsing, and service dispatch ran from the pinned source unchanged.Results
The direct control submitted:
For the client cases, the equivalent call was:
Expected
Each URL in a multipart list field should produce one same-name part, so the service should receive both values in order, matching the direct-request and local-file controls.
Source-level cause
Both client implementations iterate the list but assign URL strings through
data[name] = file. Becausedatais a mapping, each URL overwrites the previous value. Concrete local files instead use the repeated-entryfileslist. On the server side,MultipartSerde.parse_request()usesform.getlist(k), so repeated values are preserved if the client actually sends them.Affected implementations:
src/_bentoml_impl/client/http.py(_build_multipartused by HTTPX sync/async clients)src/_bentoml_impl/client/proxy2.py(duplicated aiohttp/RemoteProxy multipart builder)This report is limited to remote-schema URL-backed list inputs. Local file uploads and direct repeated multipart requests are unaffected. I am not making a security claim, and this does not appear to be an OpenAPI-schema defect.
I searched open and closed issues/PRs for the builder, URL-backed multipart lists, and last-value overwrite behavior and did not find a direct match. I have a focused service-level regression covering the direct control and all three client paths. Would a narrow fix and regression test be welcome?