Skip to content

Bug: Documentation for rewind_files() references non-existent UserMessage.uuid field #414

@nshkrdotcom

Description

@nshkrdotcom

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 later

UserMessage class definition (types.py:561-565):

@dataclass
class UserMessage:
    """User message."""

    content: str | list[ContentBlock]
    parent_tool_use_id: str | None = None

As shown above, UserMessage only has two fields: content and parent_tool_use_id. There is no uuid field.

Steps to Reproduce

  1. Follow the file checkpointing example in client.py:274-285
  2. Attempt to access msg.uuid on a UserMessage instance
  3. 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 field

message_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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdocumentationImprovements or additions to documentation

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions