From 52edd73a959a4f939018299ee2d6b46b3fded4dc Mon Sep 17 00:00:00 2001 From: Yuting Lin Date: Tue, 24 Mar 2026 13:08:03 +0000 Subject: [PATCH 1/3] fix: use load_artifact instead of file_data in rewind artifact restoration (#4932) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _compute_artifact_delta_for_rewind constructed a file_data reference (types.Part(file_data=...)) to restore artifacts to their historical version. However, GcsArtifactService raises NotImplementedError for file_data parts, and FileArtifactService has no file_data handling path, causing rewind_async to crash for all non-InMemory artifact services. Fix: use load_artifact to read the historical version's actual bytes, then save_artifact with inline_data — which all artifact service implementations support. This is consistent with how the 'artifact did not exist at rewind point' case already works (lines 750-753). Fixes #4932 --- src/google/adk/runners.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/google/adk/runners.py b/src/google/adk/runners.py index 8e352794a4..5830dc372b 100644 --- a/src/google/adk/runners.py +++ b/src/google/adk/runners.py @@ -754,15 +754,30 @@ async def _compute_artifact_delta_for_rewind( ) else: # Artifact version changed after rewind point. Restore to version at - # rewind point. - artifact_uri = artifact_util.get_artifact_uri( + # rewind point by loading the actual bytes (inline_data) rather than + # constructing a file_data reference, since GcsArtifactService and + # FileArtifactService do not support saving file_data parts. + loaded = await self.artifact_service.load_artifact( app_name=self.app_name, user_id=session.user_id, session_id=session.id, filename=filename, version=vt, ) - artifact = types.Part(file_data=types.FileData(file_uri=artifact_uri)) + if loaded and loaded.inline_data: + artifact = types.Part( + inline_data=types.Blob( + mime_type=loaded.inline_data.mime_type, + data=loaded.inline_data.data, + ) + ) + else: + # Fallback: artifact couldn't be loaded, mark as empty. + artifact = types.Part( + inline_data=types.Blob( + mime_type='application/octet-stream', data=b'' + ) + ) await self.artifact_service.save_artifact( app_name=self.app_name, user_id=session.user_id, From 853efbe08dfb2e1395a3b7cf10b8107ab7d34866 Mon Sep 17 00:00:00 2001 From: Yuting Lin Date: Tue, 24 Mar 2026 13:52:40 +0000 Subject: [PATCH 2/3] ci: retrigger CI checks From dba7c10d7d5c405dc48abcae9d0cd54057434029 Mon Sep 17 00:00:00 2001 From: Yuting Lin Date: Tue, 24 Mar 2026 23:38:24 +0000 Subject: [PATCH 3/3] refactor: address review feedback - pass loaded Part directly, add warning log - Pass load_artifact result directly to save_artifact instead of re-wrapping into a new types.Part (review by @lucasbarzotto-axonify) - Add logger.warning when fallback to empty blob is hit, to aid debugging of inconsistent artifact storage --- src/google/adk/runners.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/google/adk/runners.py b/src/google/adk/runners.py index 5830dc372b..7a3ba2d3af 100644 --- a/src/google/adk/runners.py +++ b/src/google/adk/runners.py @@ -765,14 +765,15 @@ async def _compute_artifact_delta_for_rewind( version=vt, ) if loaded and loaded.inline_data: - artifact = types.Part( - inline_data=types.Blob( - mime_type=loaded.inline_data.mime_type, - data=loaded.inline_data.data, - ) - ) + artifact = loaded else: # Fallback: artifact couldn't be loaded, mark as empty. + logger.warning( + 'Could not load artifact %s version %s for rewind;' + ' replacing with empty blob.', + filename, + vt, + ) artifact = types.Part( inline_data=types.Blob( mime_type='application/octet-stream', data=b''