Skip to content

[Windows] uvx code-review-graph daemon status raises WinError 87 even when watcher PIDs are alive #511

@Mr-B-1

Description

@Mr-B-1

Summary

On Windows, uvx code-review-graph@latest daemon status consistently raises OSError: [WinError 87] The parameter is incorrect even when the daemon's watcher PIDs are alive and the daemon is otherwise healthy. The status command is therefore unreliable as a health check on Windows.

Repro

  1. Start the daemon: uvx code-review-graph@latest daemon start (succeeds; watchers spawn)
  2. Verify watchers are alive: PowerShell Get-Process -Id <pid> returns the process for each PID in ~/.code-review-graph/daemon-state.json
  3. Query status: uvx code-review-graph@latest daemon status
  4. Observed: OSError: [WinError 87] The parameter is incorrect
  5. Expected: status report of running daemon

Root cause hypothesis

This appears to be a known CPython asyncio Proactor handle-close issue family:

PipeHandle.close() is not idempotent at the Win32 level — it calls _winapi.CloseHandle unconditionally, and if the underlying handle has already been closed between read_handle and CloseHandle, it raises. The Proactor event loop's _call_connection_lost callback then propagates the exception.

Suggested fixes (any of)

  1. Wrap with try/except OSError at the daemon-status RPC's connection-close site
  2. Pin WindowsSelectorEventLoopPolicy at process start on Windows:
    if sys.platform == "win32":
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
  3. Guard the close path with an explicit is_closed check before calling _winapi.CloseHandle

(1) is the lowest-risk surgical fix. (2) is broader but may have other implications for any subprocess code. (3) is the most correct but requires deeper plumbing.

Current workaround

I'm running a PowerShell-native status check that reads daemon-state.json directly and pings each watcher PID with Get-Process. Source available at https://github.com/Mr-B-1/workspace-control/blob/main/graph/status.ps1 — happy to upstream this as a fallback --no-rpc mode if useful.

Environment

  • OS: Windows 11
  • Python: 3.11 (Microsoft Store)
  • Package: code-review-graph@latest via uvx
  • Daemon was started and watchers verified alive at the time of the status call

Why this matters

The status check is the natural health-probe surface for the daemon, used by scheduled tasks and by humans verifying setup. The current behavior means anything checking daemon status exit code on Windows treats the daemon as broken when it isn't. Working around it requires reading the internal state file directly — fine for one user, not great as a recommendation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions