anthropics / anthropics/claude-agent-sdk-python
ClaudeSDKClient can leak transports on double connect and pre-Query failures
- 主要言語
- Python
- スター
- 8.1k
- フォーク
- 1.3k
- PR マージ指標
- PR 指標を取得中
説明
### Summary
Transport ownership is lost in two related connection lifecycle paths:
1. Calling `ClaudeSDKClient.connect()` twice overwrites the first live `Query` and transport without closing them.
2. An exception after `transport.connect()` but before a `Query` is assigned causes cleanup to discard the transport reference without closing it.
### Affected code
- `src/claude_agent_sdk/client.py:100-145`
- `src/claude_agent_sdk/client.py:187-229`
- `src/claude_agent_sdk/client.py:612-621`
- `src/claude_agent_sdk/_internal/client.py:127-174`
- `tests/test_streaming_client.py:1123-1141`
### Current behavior
`connect()` has no already-connected guard. A second call creates a new transport and assigns a new `Query`. The existing test only asserts that two transports were created; it does not assert that the first was closed.
For failures before `self._query = Query(...)`, the outer exception handler calls `disconnect()`. However, `disconnect()` only closes through `self._query`; when that is still `None`, it sets `self._transport = None` without calling `close()`.
One reachable example is an invalid `CLAUDE_CODE_STREAM_CLOSE_TIMEOUT`: integer conversion occurs after the subprocess connects but before Query construction.
The one-shot InternalClient has a similar ownership gap because `await chosen_transport.connect()` occurs before the `try/finally` that closes `query`.
### Why this matters
The abandoned transport can retain a CLI subprocess, stdin/stdout pipes, and background read tasks. Repeated failures or reconnect attempts can accumulate orphan processes and active sessions.
### Expected behavior
Every successfully or partially connected transport must have exactly one owner and must be closed on replacement or failure.
### Possible fix
- Reject a second `connect()` with `CLIConnectionError`, or explicitly disconnect the old connection before replacing it.
- Wrap the period between transport creation/connect and Query ownership transfer in a transport-level `try/finally`.
- Add assertions that the first transport is closed on double connect and that a connected transport is closed when setup fails before Query construction.
PR #1082 mentions the pre-Query transport gap as deliberately out of scope, but I could not find a dedicated issue or active fix for it.
### Environment
- Repository revision: current `main` audit at SDK version 0.2.128
- Bundled CLI version: 2.1.220
- Python test suite: 1,291 passed, 5 skipped
- Ruff and mypy: clean
I searched the existing issues and pull requests using the affected symbols and behavior before filing this.
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
評価
この issue はまだ評価されていません。