anthropics / anthropics/claude-agent-sdk-python

Background task notification leaks into next turn after receive_response()

オープン
#788 コメント 2 件 リアクション 1 件 担当者 0 名 GitHub で見る
主要言語
Python
スター
8.1k
フォーク
1.3k
平均マージ
2日 31分
マージ済み PR(30日)
1

説明

## Description

When a background task (spawned via `run_in_background=true` Agent tool) completes **between turns**, its `TaskNotificationMessage` leaks into the next `receive_response()` call. The model then responds to the stale task notification instead of the new user prompt.

## Reproduction

```python
import asyncio
import anyio
from claude_agent_sdk import (
ClaudeSDKClient, ClaudeAgentOptions,
AssistantMessage, TextBlock, ResultMessage,
TaskStartedMessage, TaskNotificationMessage,
)

async def main():
options = ClaudeAgentOptions(
allowed_tools=["Read", "Glob", "Grep", "Bash"],
permission_mode="bypassPermissions",
cwd="/tmp",
)

async with ClaudeSDKClient(options=options) as client:
# Turn 1: spawn a background task, end turn immediately
await client.query(
"Spawn a background agent (run_in_background=true) to run: sleep 60 && echo done\n"
"Then immediately say 'Spawned.' and end your turn. Do NOT wait."
)
pending = set()
async for msg in client.receive_response():
if isinstance(msg, TaskStartedMessage):
pending.add(msg.task_id)

print(f"Turn 1 done. Pending tasks: {pending}")
if not pending:
print("No pending tasks — model waited for task. Re-run to reproduce.")
return

# Wait for the background task to complete
print("Waiting 65s for background task to complete...")
await asyncio.sleep(65)

# Turn 2: simple question
await client.query("What is 2+2? Just answer with the number.")
async for msg in client.receive_response():
if isinstance(msg, TaskNotificationMessage):
print(f"*** BUG: Stale TaskNotification leaked! task_id={msg.task_id} ***")
elif isinstance(msg, AssistantMessage):
text = "".join(b.text for b in msg.content if isinstance(b, TextBlock) and b.text)
if text:
print(f"Model response: {text[:100]}")
elif isinstance(msg, ResultMessage):
result = getattr(msg, "result", "") or ""
print(f"Result: {result[:100]}")

anyio.run(main)
```

**Note:** The model may sometimes choose to wait for the background task before ending Turn 1 (no pending tasks). Re-run a few times to reproduce — it depends on model behavior.

## Expected behavior

`receive_response()` for Turn 2 should only yield messages related to Turn 2's query. Background task notifications from Turn 1 should either:
1. Be consumed by Turn 1's `receive_response()` (even after `ResultMessage`), or
2. Be filtered out / delivered through a separate channel

## Actual behavior

Turn 2's `receive_response()` yields a stale `TaskNotificationMessage` from Turn 1. The model sees this notification in its conversation context, responds to it ("The background task completed successfully...") instead of answering the new query ("2+2").

## Observed message flow

```
Turn 2 receive_response():
msg#1 TaskNotificationMessage task_id=btjnl316x status=completed ← STALE from Turn 1!
msg#2 SystemMessage subtype=init
msg#3 AssistantMessage (tool use)
msg#4 UserMessage (tool result)
msg#5 AssistantMessage "The background task completed successfully..." ← Wrong answer!
msg#6 ResultMessage
```

## Impact

In multi-turn applications (chat bots, agents), this causes:
- Model answering questions about stale background tasks instead of the user's new message
- "Answer wrong question" behavior that confuses users
- Workarounds like `client.stop_task()` are unreliable ("No task found" errors)

## Environment

- `claude-agent-sdk` 0.1.50
- Python 3.14.2
- macOS

## Related issues

- #558 (Second query hangs after background Task)
- anthropics/claude-code#21343 (Task notifications arrive after TaskOutput)

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。