anthropics / anthropics/claude-agent-sdk-python

ClaudeSDKClient can leak transports on double connect and pre-Query failures

Ouverte
#1,161 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
Langage dominant
Python
Étoiles
8.1k
Forks
1.3k
Métriques de merge des PR
Métriques de PR en attente

Description

### 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.

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.