Skip to content

Windows daemon: unbounded start_notify can wedge like the macOS daemon (#84) #89

Description

@HermannBjorgvin

Summary

The fix in #84 bounds start_notify in claude_usage_daemon.py (Linux/macOS) so a half-open BLE link can't wedge the daemon. The Windows daemon (claude_usage_daemon_windows.py) has the same bug class and is not covered by that PR.

This is a follow-up, not blocking for #84.

Details

setup_refresh_subscription() does an unbounded await self.client.start_notify(...):

https://github.com/HermannBjorgvin/Clawdmeter/blob/main/daemon/claude_usage_daemon_windows.py#L185

It sits on the path between connect and the poll loop (await session.setup_refresh_subscription() ~line 371), identical in structure to the macOS daemon. The existing except (BleakError, ValueError, OSError) does not help: an exception handler only catches a raised error, never an await that hangs forever. If WinRT's CCCD-write confirmation never arrives on a half-open link (same failure shape as the CoreBluetooth hang #84 fixed), the single-process daemon wedges between "Connected" and the first poll — stale data, no error, no recovery until manual restart.

Fix

Mirror #84: wrap the subscribe in asyncio.wait_for(..., timeout=10) and add an except asyncio.TimeoutError that logs and proceeds (the refresh subscription is optional; the 60s poll loop works without it). Keep the existing OSError in the catch list since WinRT can surface failures as raw OSError.

Not affected

The Bash daemon (claude-usage-daemon.sh) is not vulnerable: it subscribes via busctl call ... StartNotify, which is bounded by sd-bus's default ~25s method-call timeout and whose result is discarded, so it can't block indefinitely.

Ref: #84

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