Skip to content

Bug: TranscriptMirrorBatcher.enqueue's add_done_callback lambda raises CancelledError when task is cancelled #930

@seeincodes

Description

@seeincodes

Summary

src/claude_agent_sdk/_internal/transcript_mirror_batcher.py:90-91:

self._flush_task = asyncio.ensure_future(self._drain())
self._flush_task.add_done_callback(lambda t: t.exception())

The intent is "swallow the task's exception so asyncio doesn't warn about unretrieved exceptions". But in Python 3.8+, Task.exception() on a cancelled task raises CancelledError. The lambda doesn't handle it, so asyncio's callback machinery logs Exception in callback <lambda> with a CancelledError traceback every time an in-flight eager-flush task is cancelled.

Where this fires

Whenever an eager-flush task is in flight at cancellation time:

Reproducer

The traceback in #928 shows the exact symptom from a normal pytest run:

ERROR asyncio: Exception in callback TranscriptMirrorBatcher.enqueue.<locals>.<lambda>(<Task cancell...>)
  File ".../transcript_mirror_batcher.py", line 91, in <lambda>
    self._flush_task.add_done_callback(lambda t: t.exception())
                                                 ^^^^^^^^^^^^^
asyncio.exceptions.CancelledError

Impact

Log noise during normal cancellation paths. Doesn't affect functionality — the _drain task itself handles cancellation correctly via the async with self._lock. But the noise pollutes test output and production logs whenever the SDK shuts down with pending eager flushes (the entire point of session_store_flush="eager").

Suggested fix

Skip cancelled tasks before calling exception():

def _swallow_done_exception(t: asyncio.Task[None]) -> None:
    if t.cancelled():
        return
    t.exception()  # Retrieve to silence "Task exception was never retrieved"

self._flush_task.add_done_callback(_swallow_done_exception)

Equivalent one-liner:

self._flush_task.add_done_callback(
    lambda t: None if t.cancelled() else t.exception()
)

Environment

  • claude-agent-sdk-python @ 9aafd84 (current main)
  • Python 3.11.14

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions