From 06b239d8fe7ebac4e9ccf48850758de43ed3fc95 Mon Sep 17 00:00:00 2001 From: Matt Aitchison Date: Thu, 4 Jun 2026 19:14:56 -0500 Subject: [PATCH] fix: resolve file-artifact handles with uppercased uuid hex The handle regex matches hex case-insensitively, but store keys are lowercase uuid4 strings. Normalize the captured uuid to lowercase before lookup so a handle echoed by the model with uppercase hex still resolves to its bytes instead of leaking the raw token to the downstream tool. --- lib/crewai/src/crewai/tools/file_artifact.py | 5 ++++- lib/crewai/tests/tools/test_file_artifact.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/crewai/src/crewai/tools/file_artifact.py b/lib/crewai/src/crewai/tools/file_artifact.py index 74c17994f..db4838284 100644 --- a/lib/crewai/src/crewai/tools/file_artifact.py +++ b/lib/crewai/src/crewai/tools/file_artifact.py @@ -187,7 +187,10 @@ def resolve_artifact_handles(value: Any) -> Any: return value def _sub(match: re.Match[str]) -> str: - artifact = _store.resolve(match.group(1)) + # Store keys are lowercase uuid4 strings; the regex matches hex + # case-insensitively, so normalize before lookup in case the model + # echoed the handle with uppercase hex. + artifact = _store.resolve(match.group(1).lower()) return artifact.as_base64() if artifact is not None else match.group(0) return _HANDLE_RE.sub(_sub, value) diff --git a/lib/crewai/tests/tools/test_file_artifact.py b/lib/crewai/tests/tools/test_file_artifact.py index f19160128..74f1965be 100644 --- a/lib/crewai/tests/tools/test_file_artifact.py +++ b/lib/crewai/tests/tools/test_file_artifact.py @@ -100,6 +100,16 @@ class TestResolveArtifactHandles: resolved = resolve_artifact_handles(handle) assert base64.b64decode(resolved) == data + def test_resolves_handle_with_uppercased_hex(self) -> None: + # A model may echo the handle with uppercase uuid hex; lookup must still + # hit the lowercase-keyed store. + data = b"upper-case-payload" * 100 + handle = _handle_in(store_artifact(FileArtifact(data=data))) + scheme, _, hex_part = handle.rpartition("/") + upper = f"{scheme}/{hex_part.upper()}" + assert upper != handle + assert base64.b64decode(resolve_artifact_handles(upper)) == data + def test_resolves_handle_inside_dict(self) -> None: data = b"binary-payload" * 1000 handle = _handle_in(store_artifact(FileArtifact(data=data)))