Skip to content

fix: handle tuples in deepcopy_minimal to prevent dict mutation#1226

Open
giulio-leone wants to merge 1 commit intoanthropics:mainfrom
giulio-leone:fix/deepcopy-minimal-tuple-mutation
Open

fix: handle tuples in deepcopy_minimal to prevent dict mutation#1226
giulio-leone wants to merge 1 commit intoanthropics:mainfrom
giulio-leone:fix/deepcopy-minimal-tuple-mutation

Conversation

@giulio-leone
Copy link

Summary

Fixes #1202deepcopy_minimal does not recurse into tuples, causing files.beta.upload to mutate the caller's headers dict in-place.

Root Cause

deepcopy_minimal only handles dict and list, returning everything else as-is. When a FileTypes tuple like (name, content, mime, headers_dict) is passed, the tuple is shared by reference, so any downstream mutation of the headers dict (element [3]) leaks back to the caller.

Fix

Add tuple handling to deepcopy_minimal:

if isinstance(item, tuple):
    return cast(_T, tuple(deepcopy_minimal(entry) for entry in item))

This ensures dicts (and lists) nested inside tuples are recursively copied.

Tests

`deepcopy_minimal` only recursed into dicts and lists, ignoring tuples.
When a `FileTypes` tuple like `(name, content, mime, headers_dict)` was
passed to `files.beta.upload`, the headers dict inside the tuple was
shared between the original and the copy, causing in-place mutation of
the caller's data.

Add tuple handling so dicts nested inside tuples are properly copied.

Fixes anthropics#1202

Signed-off-by: Giulio Leone <6887247+giulio-leone@users.noreply.github.com>
@giulio-leone giulio-leone requested a review from a team as a code owner March 8, 2026 02:05
@giulio-leone
Copy link
Author

Friendly ping — CI is green, tests pass, rebased on latest. Ready for review whenever convenient. Happy to address any feedback. 🙏

@giulio-leone
Copy link
Author

Added a real runtime validation pass for this fix using the public Anthropic(...).beta.files.upload(file=...) path against a local HTTP server (no mocks/stubs in the reproduction itself).

Results:

  • On upstream/main, the caller-owned headers dict was mutated in place by the upload call: {'X-Original': '1'} became {'X-Original': '1', 'Content-Type': 'text/plain'} after the request completed.\n- On this branch, the exact same upload call returned a valid FileMetadata response and left the original headers dict unchanged.\n- Targeted regression suite still passes locally: .venv/bin/python -m pytest tests/test_deepcopy.py -q -> 8 passed.\n\nSo this is now backed by both the focused tests and a real before/after reproduction of the public upload flow from issue Use of deepcopy_minimal in files.beta.upload mutates dict in place #1202.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use of deepcopy_minimal in files.beta.upload mutates dict in place

1 participant