Skip to content

Windows 11 pairing prompt causes Bleak connect failures (0x800704C7, 0x80000013) with Raspberry Pi (BlueZ/BLESS) #1880

@startupreboot

Description

@startupreboot

Summary

After upgrading to Windows 11, a Python BLE client using Bleak (WinRT backend) fails to connect/pair to a Raspberry Pi 4 BLE GATT server (Python Bless / BlueZ). Windows shows a system pairing prompt (“Pair device?”). When the user clicks Allow, Windows immediately shows “Connection failed”. On the client side, Bleak throws:

  • WinError -2147023673 (0x800704C7) — “The operation was canceled by the user”

  • Often followed by WinError -2147483629 (0x80000013) — “The object has been closed”

This happens even when the user actively clicks Allow on the Windows pairing UI.

Environment

  • Client OS: Windows 11
  • Client library: Python + Bleak (WinRT)
  • Peripheral: Raspberry Pi 4
  • Server: Python + Bless (BlueZ backend)
  • Transport: BLE GATT

Observed Behavior

  1. Client calls await client.connect() (and/or immediately does await client.get_services() after connect).
  2. Windows 11 displays a pairing/consent dialog.
  3. User clicks Allow.
  4. Windows shows Connection failed immediately.
  5. Client receives 0x800704C7 (“canceled by user”) even though user allowed.

Expected Behavior

  • Pairing should complete successfully when user clicks Allow, and Bleak should proceed with a connected GATT session (or return a clear pairing failure reason if the peripheral rejects pairing).

Evidence / Logs

Windows UI

  • Dialog: “<DeviceName> would like to pair… Allow / Cancel”
  • After Allow: “Connection failed” shown immediately.

Raspberry Pi output (representative)

  • BLE advertising starts successfully.
  • When attempting to configure pairing agent via scripting:
    • Bluetooth command agent NoInputNoOutput executed successfully.
    • No agent is registered
    • Skip: Command '['bluetoothctl', 'default-agent']' returned non-zero exit status 1.
  • Then device becomes:
    • discoverable, pairable, bondable, secure-conn, etc.
      …but pairing from Windows still fails.

Reproduction Steps

  1. Start BLE GATT server on Pi (Bless/BlueZ).
  2. On Windows 11, run Python Bleak client and attempt connect.
  3. Observe Windows pairing prompt.
  4. Click Allow.
  5. Observe immediate failure and 0x800704C7 on client.

Sample Code

A) Raspberry Pi: Recommended bluetoothctl CLI sequence (manual)

Run in a single interactive session and keep it open during pairing:

sudo bluetoothctl
power on
agent NoInputNoOutput
default-agent
pairable on
discoverable on

Pair device independently using the Bluetooth & devices

B) Raspberry Pi: Persistent bluetoothctl agent example (Python)

This avoids the “agent gets lost because bluetoothctl exits” issue by keeping one bluetoothctl process alive.

import subprocess
import time

class BtCtl:
    def __init__(self):
        self.p = subprocess.Popen(
            ["bluetoothctl"],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            text=True,
            bufsize=1
        )

    def cmd(self, s: str, delay: float = 0.2):
        assert self.p.stdin is not None
        self.p.stdin.write(s + "\n")
        self.p.stdin.flush()
        time.sleep(delay)

def setup_bluetooth_persistent():
    bt = BtCtl()
    bt.cmd("power on")
    bt.cmd("agent NoInputNoOutput")
    bt.cmd("default-agent")
    bt.cmd("discoverable on")
    bt.cmd("pairable on")
    return bt  # IMPORTANT: keep this alive while pairing is needed

if __name__ == "__main__":
    bt = setup_bluetooth_persistent()
    print("Agent is running; keep this process alive while pairing from Windows.")
    while True:
        time.sleep(5)

C) Windows 11: Bleak client connect example

Includes increased timeouts and “discover services after connect”. If pairing is required, you may need to request pairing (depending on Bleak version).

import asyncio
from bleak import BleakScanner, BleakClient

ADDRESS = "D8:3A:DD:AC:C6:5D"  # example

async def main():
    # Prefer: find BLEDevice via scan (more reliable than address-only)
    device = await BleakScanner.find_device_by_address(ADDRESS, timeout=20.0)
    if not device:
        print("Device not found")
        return

    client = BleakClient(device, pair=True)  

    try:
        await client.connect(timeout=30.0)

        # On Windows, service discovery may trigger pairing if required by permissions
        services = await client.get_services()
        print("Services:", services)

    finally:
        try:
            await client.disconnect()
        except Exception:
            pass

asyncio.run(main())

  1. Is 0x800704C7 on Windows 11 commonly returned when the OS pairing prompt fails (even after user clicks Allow)?

  2. Are there known changes in Windows 11 WinRT BLE pairing that require:

    • connecting via BLEDevice only,
    • explicit pair() before get_services(),
    • longer timeouts,
    • or specific WinRT APIs not currently handled by Bleak?
  3. Does Bleak have recommended patterns to avoid triggering pairing during connect()/get_services() on Windows (especially for Pi/BlueZ peripherals)?


Additional Context

  • Windows UI consistently shows pairing prompt, and then immediate “Connection failed” after clicking Allow.
  • The client then throws WinError -2147023673 (“operation canceled by user”).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Backend: WinRTIssues or PRs relating to the WinRT backendmore info requiredIssues does not have a reproducible test case, has insufficent logs or otherwise needs more feedbackpairingIssues related to pairing/bonding

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions