Repository navigation
Runner._compute_artifact_delta_for_rewind saves artifacts with "file_data", which GcsArtifactService and FileArtifactService don't supportΒ #4932
Description
π΄ Required Information
Describe the Bug:
Runner._compute_artifact_delta_for_rewind (runners.py:755β765) restores historical artifact versions by constructing a types.Part(file_data=types.FileData(file_uri=...)) and passing it to artifact_service.save_artifact. However, GcsArtifactService._save_artifact explicitly raises NotImplementedError when it receives a file_data part (gcs_artifact_service.py:232β236), and FileArtifactService has no file_data handling path at all.
This means rewind_async crashes at runtime for any application using GcsArtifactService or FileArtifactService when the session contains artifacts that changed after the rewind point. Only InMemoryArtifactService works, since it stores the Part object as-is without attempting to extract bytes.
Steps to Reproduce:
- Create an ADK application using
GcsArtifactService(orFileArtifactService) - Run agent invocations that produce artifacts (e.g. save an image via
tool_context.save_artifact) - Run additional invocations that modify or create new versions of the same artifact
- Call
runner.rewind_async(user_id=..., session_id=..., rewind_before_invocation_id="<invocation_before_artifact_change>") - Observe
NotImplementedErrorfromGcsArtifactService
NotImplementedError: Saving artifact with file_data is not supported yet in GcsArtifactService.
Expected Behavior:
rewind_async should successfully restore artifacts to their historical version regardless of which ArtifactService implementation is used. Since load_artifact already returns artifact content as inline_data (it reads the actual bytes from storage), the rewind code should use load_artifact to read the historical version and then save_artifact with inline_data β which all artifact service implementations support.
Observed Behavior:
rewind_async raises NotImplementedError when any artifact needs restoration and the artifact service is GcsArtifactService:
NotImplementedError: Saving artifact with file_data is not supported yet in GcsArtifactService.
The crash occurs at runners.py:766 when calling self.artifact_service.save_artifact(... artifact=artifact ...) with the file_data part constructed at line 765.
Root Cause:
In runners.py:755β765, _compute_artifact_delta_for_rewind constructs an internal artifact:// URI and wraps it in file_data:
artifact_uri = artifact_util.get_artifact_uri(
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))
await self.artifact_service.save_artifact(
app_name=self.app_name,
user_id=session.user_id,
session_id=session.id,
filename=filename,
artifact=artifact,
)But GcsArtifactService._save_artifact (gcs_artifact_service.py:232β236) rejects file_data:
elif artifact.file_data:
raise NotImplementedError(
"Saving artifact with file_data is not supported yet in"
" GcsArtifactService."
)And FileArtifactService._save_artifact only handles inline_data and text β a file_data part falls through to the "Artifact must have either inline_data or text" validation error.
Suggested Fix:
Instead of constructing a file_data reference, the rewind code should load_artifact at the target version (which returns the actual bytes as inline_data) and then save_artifact with that inline_data part. This works with all artifact service implementations:
# Instead of constructing a file_data reference:
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,
)
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"")
)Note: the code already uses inline_data for the "artifact did not exist at rewind point" case (runners.py:750β753) β the fix just extends this approach to the "restore existing version" case.
Environment Details:
- ADK Library Version: 1.27.1
- Desktop OS: macOS (darwin 24.6.0)
- Python Version: 3.13
Model Information:
- Are you using LiteLLM: No
- Which model is being used: gemini-2.5-pro
π‘ Optional Information
How often has this issue occurred?:
- Always (100%) β occurs whenever
rewind_asyncneeds to restore an artifact and the artifact service isGcsArtifactServiceorFileArtifactService
Activity
fix: use load_artifact instead of file_data in rewind artifact restorβ¦
surajksharma07 commentedon Mar 24, 2026
@lucasbarzotto-axonify The issue is that _compute_artifact_delta_for_rewind builds a file_data URI reference and passes it directly to save_artifact, but neither FileArtifactService nor GcsArtifactService have any handler for that format β so it falls over before it can do anything useful.
A workaround worth trying: load the artifact at the target version via load_artifact(version=vt) and re-save using its inline_data instead.
That path works consistently across all artifact service implementations, so it should hold up regardless of which backend you're running.
Could you verify whether that clears the crash on your end before we finalize PR #4979?
Team is looking more into it.
lucasbarzotto-axonify commentedon Mar 24, 2026
Hi @surajksharma07, thanks for confirming the issue.
Yes, I've run a local test and confirmed that this change fixes the problem. I've also added a couple of minor comments on the PR.
Thanks!
surajksharma07 commentedon Mar 25, 2026
@lucasbarzotto-axonify Thanks for verifying the fix and for the review comments on PR #4979 β really appreciate the thorough testing.
The team will take a look at your comments on the PR and get it finalized.
surajksharma07 commentedon Mar 25, 2026
@DeanChensj Please have a look inton it once.
refactor: Use artifact_service.load_artifact during rewind
refactor: Use artifact_service.load_artifact during rewind