anthropics / anthropics/claude-agent-sdk-python

fork_session() write is non-atomic; crash mid-write leaves orphan transcript

Đang mở
#906 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Python
Star
8.1k
Fork
1.3k
Merge trung bình
2 ngày 31 phút
Pull request đã merge (30 ngày)
1

Mô tả

## Summary

`fork_session()` writes the new transcript with a single `os.write()` call without a tmp+rename, so a crash between `os.open` and `os.close` leaves a partial or empty `.jsonl` in the project directory.

## Source

In `claude_agent_sdk/_internal/session_mutations.py` (verified against 0.1.72 on PyPI):

```python
fork_path = project_dir / f"{forked_session_id}.jsonl"
fd = os.open(fork_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
try:
os.write(fd, ("\n".join(lines) + "\n").encode("utf-8"))
finally:
os.close(fd)
```

There is no temp file + `os.replace()` step, so a process kill between `os.open` and the completed `os.write` produces a half-written file. `O_EXCL` guarantees the file did not exist beforehand, but does not provide atomicity for the contents.

## Impact

Low severity:

- Original session file is untouched (no data loss).
- The crash window is short — `os.write()` of typical transcript sizes (KB to ~100KB) completes in microseconds — but it is not zero.
- The orphan `.jsonl` will appear in any directory listing and may surface in tooling that enumerates sessions, until manually cleaned.
- Forked-session UUIDs are random v4, so collisions with a later successful fork are not a practical concern, but the orphan file occupies its UUID slot until removed.

## Suggested fix

Standard tmp + atomic rename:

```python
fork_path = project_dir / f"{forked_session_id}.jsonl"
tmp_path = fork_path.with_suffix(".jsonl.tmp")
fd = os.open(tmp_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
try:
os.write(fd, ("\n".join(lines) + "\n").encode("utf-8"))
finally:
os.close(fd)
os.replace(tmp_path, fork_path)
```

`os.replace()` is atomic on POSIX and on Windows (when source and destination are on the same volume, which they are here since both paths are inside `project_dir`).

## Reproduction

The behavior is visible from source inspection alone; a deterministic mid-write crash repro would require injecting a fault into `os.write`. The same code shape that makes it non-atomic also makes a fault-injection repro straightforward in a test (mock `os.write` to raise mid-call).

## Version

claude-agent-sdk 0.1.72 (latest on PyPI as of filing).

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.