code-yeongyu / code-yeongyu/pi-cua-integration

Windows: daemon emits Proactor WinError 6 when reading stdin during reload

Open
#31 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
6
Forks
3
PR merge metrics
No merged PRs in 30d

Description

## Summary

On Windows with Python 3.13, the daemon reports ready successfully but emits a large asyncio traceback during reload or shutdown:

```text
Exception in callback _ProactorReadPipeTransport._loop_reading()
OSError: [WinError 6] The handle is invalid
AttributeError: '_ProactorReadPipeTransport' object has no attribute '_empty_waiter'
```

The daemon currently connects `asyncio` directly to `sys.stdin`:

```python
reader = asyncio.StreamReader(loop=loop)
protocol = asyncio.StreamReaderProtocol(reader)
await loop.connect_read_pipe(lambda: protocol, sys.stdin)
```

Windows' Proactor event loop cannot reliably register this standard-input handle with IOCP. This is the same limitation documented in:

- https://github.com/python/cpython/issues/87694
- https://github.com/python/cpython/issues/71019

## Steps to reproduce

1. Configure the extension to use Python 3.13 on Windows.
2. Install a compatible Cua SDK and reload the extension.
3. Confirm the daemon reports:

```text
[pi-cua] ready (mode=local, ...)
```

4. Reload the extension again or shut down the daemon.
5. Observe the `_ProactorReadPipeTransport` traceback on stderr.

## Expected behavior

Daemon startup, reload, and shutdown should complete without Python stderr output.

## Actual behavior

The daemon becomes ready, but its stdin transport emits `WinError 6` and then an `_empty_waiter` `AttributeError`.

## Suggested fix

Avoid `connect_read_pipe()` for standard input and perform each blocking read in asyncio's worker thread:

```python
while not self._stop.is_set():
line = await asyncio.to_thread(sys.stdin.buffer.readline)
if not line:
break
# existing JSON-RPC parsing and dispatch
```

This preserves the async request handlers without using the unsupported Windows Proactor stdin transport.

Add an integration regression test that:

1. Starts the real Python daemon through `startDaemon()`.
2. Captures `onLog`/stderr before startup.
3. Waits for the ready event.
4. Calls `shutdown()`.
5. Asserts `cuaAvailable === true`, exit is clean, and captured stderr is empty.

The test should use the configured `PI_CUA_PYTHON` interpreter and run in Windows CI with Python 3.13.

## Acceptance criteria

- Two consecutive daemon start/shutdown cycles complete on Windows.
- Both cycles report ready.
- Neither cycle emits `_ProactorReadPipeTransport`, `WinError 6`, or any other stderr traceback.
- Existing JSON-RPC integration tests continue to pass.

Contributor guide

Open the contributing guide

Research direction

Start with the daemon's current connect_read_pipe stdin path and the startDaemon()/shutdown() integration entry points; use PI_CUA_PYTHON and existing JSON-RPC integration tests as the map. Run the Windows CI scenario with Python 3.13 and verify two start/shutdown cycles reach ready, exit cleanly, and capture no stderr traceback.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, typescript
Domain
backend, operating-systems, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.