-
Notifications
You must be signed in to change notification settings - Fork 537
Description
Description
The documentation for the rewind_files() method instructs users to access msg.uuid from a UserMessage instance, but the UserMessage class does not have a uuid field. This causes an AttributeError when following the documented example.
Location of the Bug
Documentation (client.py:280-281):
async for msg in client.receive_response():
if isinstance(msg, UserMessage):
checkpoint_id = msg.uuid # Save this for laterUserMessage class definition (types.py:561-565):
@dataclass
class UserMessage:
"""User message."""
content: str | list[ContentBlock]
parent_tool_use_id: str | None = NoneAs shown above, UserMessage only has two fields: content and parent_tool_use_id. There is no uuid field.
Steps to Reproduce
- Follow the file checkpointing example in
client.py:274-285 - Attempt to access
msg.uuidon aUserMessageinstance - Observe
AttributeError: 'UserMessage' object has no attribute 'uuid'
Minimal reproduction:
from claude_agent_sdk.types import UserMessage
user_msg = UserMessage(content="test", parent_tool_use_id=None)
checkpoint_id = user_msg.uuid # AttributeError!Full reproduction script
"""
Minimal reproduction script for Python SDK file checkpointing bug.
This script demonstrates that the documented usage of msg.uuid on UserMessage
fails because UserMessage class does not have a uuid field.
Bug location:
- Documentation: client.py:280-281
- UserMessage class: types.py:561-565
The documentation says to use msg.uuid from UserMessage, but UserMessage
does not have a uuid field - only StreamEvent has this field.
This script uses mock classes to demonstrate the bug without requiring
the actual SDK to be installed.
"""
from dataclasses import dataclass
from typing import Any
# Mock versions of the SDK classes to demonstrate the bug
# These match the actual class definitions in types.py
@dataclass
class UserMessage:
"""User message - EXACTLY as defined in types.py:561-565"""
content: str | list[Any]
parent_tool_use_id: str | None = None
# NOTE: No uuid field!
@dataclass
class StreamEvent:
"""Stream event - EXACTLY as defined in types.py:603-609"""
uuid: str
session_id: str
event: dict[str, Any]
parent_tool_use_id: str | None = None
def demonstrate_bug():
"""Demonstrate the documented usage that fails."""
print("="*70)
print("DEMONSTRATING THE BUG")
print("="*70)
# Create a UserMessage instance as it would be created by the parser
# (simplified version from message_parser.py:75-78)
user_msg = UserMessage(
content="test message",
parent_tool_use_id=None
)
print("\n1. UserMessage instance created successfully")
print(f" UserMessage fields: {list(user_msg.__dataclass_fields__.keys())}")
print(f" Fields are: {', '.join(user_msg.__dataclass_fields__.keys())}")
# This is what the documentation says to do (client.py:280-281):
# "if isinstance(msg, UserMessage):
# checkpoint_id = msg.uuid # Save this for later"
print("\n2. Attempting to access msg.uuid as documented in client.py:280-281...")
print(" Documentation says:")
print(" if isinstance(msg, UserMessage):")
print(" checkpoint_id = msg.uuid # Save this for later")
print()
try:
checkpoint_id = user_msg.uuid
print(f" SUCCESS! checkpoint_id = {checkpoint_id}")
except AttributeError as e:
print(f" FAILED with AttributeError: {e}")
print("\n ** THIS IS THE BUG **")
print(" UserMessage has NO 'uuid' field!")
# Show that StreamEvent has the uuid field
print("\n" + "="*70)
print("For comparison, StreamEvent DOES have a uuid field:")
print("="*70)
stream_event = StreamEvent(
uuid="test-uuid-123",
session_id="session-456",
event={},
parent_tool_use_id=None
)
print(f"\nStreamEvent fields: {list(stream_event.__dataclass_fields__.keys())}")
print(f"StreamEvent.uuid = {stream_event.uuid}")
print("\nStreamEvent correctly has 'uuid' field, but UserMessage does not!")
print("="*70)
if __name__ == "__main__":
demonstrate_bug()Expected vs Actual Behavior
Expected: The documented example should work without errors, allowing users to save a checkpoint ID from user messages.
Actual: Following the documented example raises AttributeError because UserMessage has no uuid field.
Analysis
Looking at the message parser (message_parser.py:48-86), when parsing a user message, the parser creates a UserMessage instance without extracting or passing a uuid field from the raw message data:
case "user":
# ... parsing logic ...
return UserMessage(
content=user_content_blocks,
parent_tool_use_id=parent_tool_use_id,
)In contrast, StreamEvent (types.py:603-609) does have a uuid field and the parser correctly extracts it (message_parser.py:162-167):
case "stream_event":
return StreamEvent(
uuid=data["uuid"],
session_id=data["session_id"],
event=data["event"],
parent_tool_use_id=data.get("parent_tool_use_id"),
)Note: The raw CLI output DOES include a uuid field for user messages (confirmed via testing), but it is not being extracted by the parser.
Suggested Fix
Option 1: Add uuid field to UserMessage (Recommended)
Add a uuid field to the UserMessage dataclass and update the parser to extract it:
types.py:
@dataclass
class UserMessage:
"""User message."""
content: str | list[ContentBlock]
parent_tool_use_id: str | None = None
uuid: str | None = None # Add uuid fieldmessage_parser.py:
case "user":
# ... existing parsing logic ...
return UserMessage(
content=user_content_blocks,
parent_tool_use_id=parent_tool_use_id,
uuid=data.get("uuid"), # Extract uuid from raw data
)Option 2: Update documentation
If there's a reason UserMessage shouldn't have a uuid field, update the documentation to show the correct approach for obtaining checkpoint IDs.
Additional Context
- SDK Version: 0.1.15+
- This bug affects the file checkpointing feature added in v0.1.15
- Users following the documentation will encounter immediate runtime errors when trying to use this feature