From 3ab5357027f9a0029937ff4d602955e792ad780f Mon Sep 17 00:00:00 2001 From: blut-agent Date: Sat, 16 May 2026 02:50:43 -0500 Subject: [PATCH] fix: copy mutable file tuple containers to prevent shared state File upload helpers were sharing mutable containers nested inside file tuples, such as per-file headers dictionaries. This could cause bugs when uploading multiple files or when the caller reuses file input dictionaries. - Add `_transform_file_tuple` helper that copies dict entries (headers) while preserving immutable file content references. - Use the helper in both `_transform_file` and `_async_transform_file`. - Add regression test for file tuple with mutable headers. Fixes #1548 --- src/anthropic/_files.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/anthropic/_files.py b/src/anthropic/_files.py index 42cfbeb99..142268812 100644 --- a/src/anthropic/_files.py +++ b/src/anthropic/_files.py @@ -71,11 +71,24 @@ def _transform_file(file: FileTypes) -> HttpxFileTypes: return file if is_tuple_t(file): - return (file[0], read_file_content(file[1]), *file[2:]) + return cast(HttpxFileTypes, _transform_file_tuple(file)) raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") +def _transform_file_tuple(file: tuple[object, ...]) -> tuple[object, ...]: + # Copy mutable entries in file tuples to prevent shared state. + # File tuples can be: (filename, content), (filename, content, content_type), + # or (filename, content, content_type, headers) where headers is a mutable Mapping. + result: list[object] = [file[0], read_file_content(file[1])] + for item in file[2:]: + if isinstance(item, dict): + result.append(dict(item)) + else: + result.append(item) + return tuple(result) + + def read_file_content(file: FileContent) -> HttpxFileContent: if isinstance(file, os.PathLike): return pathlib.Path(file).read_bytes() @@ -113,7 +126,7 @@ async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: return file if is_tuple_t(file): - return (file[0], await async_read_file_content(file[1]), *file[2:]) + return cast(HttpxFileTypes, _transform_file_tuple((file[0], await async_read_file_content(file[1]), *file[2:]))) raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")