Pre-flight checklist
Which part of OpenLogi?
Both
OpenLogi version
v0.6.22 release, exact tag/commit:
v0.6.22
b6aaf468d2a7a84c2579955c9b9287c4b4c7dd21
Operating system
macOS
OS version & architecture
macOS 27.0, Apple Silicon (arm64)
Device model
Logitech MX Anywhere 3S, VID 046d, PID b037
How is the device connected?
Bluetooth (direct, no receiver)
Affected area(s)
What happened?
The MX Anywhere 3S is paired and works as a normal macOS mouse, but OpenLogi usually shows it as offline or reports zero detected devices. Settings such as reverse scrolling do not take effect because the agent never establishes a usable inventory/control route.
The device is supported and permissions are not the blocker:
- macOS exposes the expected Logitech HID++ long-report collection, usage page
0xff43, usage 0x0202.
- OpenLogi opens that interface successfully.
- Accessibility and Input Monitoring are granted to the signed nested
OpenLogiAgent.app.
- Many HID++ requests return normally in about 15–30 ms.
- A standalone
openlogi list trace eventually completed the same 35-entry feature walk and correctly identified the mouse, including battery and BTLE transport.
The failure occurs when any one request during the feature-table walk receives no response. The failed slot varies between attempts. enumerate_features() returns an error, probe_features() discards all features already read, and probe_direct() then classifies the Bluetooth-direct mouse as a likely receiver secondary interface:
enumerate_features failed slot=255 error=Channel(Timeout)
slot 0xff exposes no battery or config feature — likely a receiver secondary interface; skipping vid=046d pid=b037 has_model=false
The persistent agent then evicts/reopens the channel and repeats this indefinitely. The GUI retains a saved device record but marks it offline, or reports no device.
This makes a recoverable dropped BLE HID++ response become a complete device-discovery failure. It also explains why killing/restarting the agent or rescanning sometimes appears to fix the device: a later attempt may happen to complete every request.
Expected behavior
A single feature-table request timeout should be retried or tolerated. A Bluetooth-direct node that was opened successfully and returned valid HID++ responses should not be discarded and described as a receiver secondary interface solely because one later feature-table request timed out.
At minimum, OpenLogi should retain/replay the last known direct-device identity and capabilities while retrying the failed request.
Steps to reproduce
-
Pair an MX Anywhere 3S directly through macOS Bluetooth.
-
Fully quit/uninstall Logi Options+ and ensure no competing Logitech process is running.
-
Grant Accessibility and Input Monitoring to OpenLogi Agent.
-
Quit the GUI and agent.
-
Run:
OPENLOGI_LOG=trace /Applications/OpenLogi.app/Contents/MacOS/openlogi list
-
Repeat if necessary. Some attempts read part of the 35-entry feature table and then time out; another attempt may complete.
-
Start openlogi-agent with OPENLOGI_LOG=trace. In this environment it repeatedly aborts the feature walk, evicts the channel, and never logs BT-direct / wired device recognised.
-
Reopen the GUI. The saved MX Anywhere 3S remains offline and device settings do not take effect.
Diagnostics report
### OpenLogi Diagnostics
**App**
- OpenLogi (GUI): v0.6.22 (release)
- Agent: v0.6.22 (connected)
- IPC protocol: GUI 10 / agent 10
- Inventory: ready
- OS: macOS 27.0 (arm64)
- Locale: en-CN (UI: follow system)
- Accessibility: granted · Input hook: installed
- Launch at login: no · Menu bar: yes · Update check: off
- Running from: source build (dev)
- Config: schema 3 · 0 configured device(s) · thumbwheel 14
**Assets**
- Source: user cache · Index: loaded (210 models) · User cache: present
- Cache path: ~/.local/share/openlogi/assets · Bundle assets: absent
**Devices (0)**
- No devices detected.
openlogi list output
An exclusive trace demonstrates that the exact same device can eventually be identified when one retry completes:
DEBUG openlogi_hid::transport: logitech HID node name=MX Anywhere 3S pid=b037 usage_page=0xff43 usage_id=0x0202 matched=true
DEBUG openlogi_hid::inventory: HID++ candidate interfaces count=1
DEBUG openlogi_hid::transport: opened HID++ channel name=MX Anywhere 3S vid=046d
...
TRACE hidpp::device: feature index=255 slot=35 id=0x9205 version=0
...
DEBUG openlogi_hid::inventory::probe: BT-direct / wired device recognised name=MX Anywhere 3S
MX Anywhere 3S (—, vid=046d pid=b037)
└─ slot 255 ● MX Anywhere 3S (mouse, wpid=?, battery=40% good (discharging))
model_ids=[b037,0000,0000] ext=00 serial=[redacted] unit_id=[redacted] transports=btle
Earlier retries in that same process failed:
TRACE hidpp::channel: hidpp no response dev=255 feat=2 error=Timeout
...
TRACE hidpp::channel: hidpp no response dev=255 feat=1 error=Timeout
DEBUG openlogi_hid::inventory::features: enumerate_features failed slot=255 error=Channel(Timeout)
DEBUG openlogi_hid::inventory::probe: slot 0xff exposes no battery or config feature — likely a receiver secondary interface; skipping vid=046d pid=b037 has_model=false
Detailed persistent-agent trace
The agent was launched alone (GUI closed), so this happens before gesture/control capture becomes active.
Attempt 1 reached slot 12, then timed out:
TRACE hidpp::device: enumerating feature table index=255 count=35
TRACE hidpp::device: feature index=255 slot=10 id=0x1b04 version=5
TRACE hidpp::device: feature index=255 slot=11 id=0x1814 version=1
TRACE hidpp::device: feature index=255 slot=12 id=0x1815 version=2
TRACE hidpp::channel: hidpp request dev=255 feat=1 func=17
# approximately five seconds
TRACE hidpp::channel: hidpp no response dev=255 feat=1 error=Timeout
DEBUG openlogi_hid::inventory::features: enumerate_features failed slot=255 error=Channel(Timeout)
DEBUG openlogi_hid::inventory::probe: slot 0xff exposes no battery or config feature — likely a receiver secondary interface; skipping vid=046d pid=b037 has_model=false
A later attempt failed immediately after slot 4:
TRACE hidpp::device: enumerating feature table index=255 count=35
TRACE hidpp::device: feature index=255 slot=1 id=0x0001 version=2
TRACE hidpp::device: feature index=255 slot=2 id=0x0003 version=4
TRACE hidpp::device: feature index=255 slot=3 id=0x0005 version=0
TRACE hidpp::device: feature index=255 slot=4 id=0x1d4b version=0
TRACE hidpp::channel: hidpp request dev=255 feat=1 func=17
# approximately five seconds
TRACE hidpp::channel: hidpp no response dev=255 feat=1 error=Timeout
DEBUG openlogi_hid::inventory::features: enumerate_features failed slot=255 error=Channel(Timeout)
Another reached slot 20 before failing:
TRACE hidpp::device: feature index=255 slot=17 id=0x2251 version=0
TRACE hidpp::device: feature index=255 slot=18 id=0x00c3 version=0
TRACE hidpp::device: feature index=255 slot=19 id=0x1802 version=0
TRACE hidpp::device: feature index=255 slot=20 id=0x1803 version=0
TRACE hidpp::channel: hidpp request dev=255 feat=1 func=17
# approximately five seconds
TRACE hidpp::channel: hidpp no response dev=255 feat=1 error=Timeout
Observed failure points across consecutive attempts included immediately after slots 0, 1, 4, 6, 9, 10, 12, 15, 16, and 20. That varying position argues against one unsupported feature ID.
Some channel writes also returned:
TRACE hidpp::channel: hidpp no response dev=255 feat=0 error=Implementation(Message("report writer callback error: 0xE00002D6"))
DEBUG openlogi_hid::inventory::features: Device::new failed slot=255 error=Channel(Implementation(Message("report writer callback error: 0xE00002D6")))
Apple's local IOKit IOReturn.h defines 0xE00002D6 as kIOReturnTimeout (I/O Timeout), not kIOReturnNotPermitted.
After two failed ticks:
WARN openlogi_hid::inventory: node probe keeps failing — dropping its channel to reopen next tick
This cycle repeated for more than a minute without producing:
BT-direct / wired device recognised
control capture active
Therefore the initial failure is reproducible without GUI/agent contention and before the gesture capture watcher can open a second channel.
Relevant v0.6.22 code path
-
crates/openlogi-hidpp/src/device.rs:147-149
for i in 1..=count {
let info = feature_set_feature.get_feature(i).await?;
A single failed request aborts the full walk; there is no per-entry retry/backoff.
-
crates/openlogi-hid/src/inventory/features.rs:91-101
Any enumerate_features() error immediately returns ProbedFeatures::default(), discarding successfully read entries.
-
crates/openlogi-hid/src/inventory/probe.rs:436-455
Empty capabilities/battery make is_peripheral false, so the direct BLE mouse is logged as a likely receiver secondary interface and returned with no inventory.
Possible fix direction
- Retry an individual feature-table request one or two times, with a short BLE-appropriate delay/backoff, rather than restarting the complete node probe.
- Preserve the partial/last-known feature table and direct-device identity when a later entry times out.
- Do not apply the receiver-secondary discriminator to a direct node when the feature walk itself failed; report it as a transient probe failure instead.
- Avoid repeated open/close churn if channel eviction is amplifying the macOS IOHID timeout state.
Related: #159, #218, #254, #322, #348, and #454.
macOS permissions
Pre-flight checklist
b6aaf468d2a7a84c2579955c9b9287c4b4c7dd21).Which part of OpenLogi?
Both
OpenLogi version
v0.6.22 release, exact tag/commit:
Operating system
macOS
OS version & architecture
macOS 27.0, Apple Silicon (arm64)
Device model
Logitech MX Anywhere 3S, VID
046d, PIDb037How is the device connected?
Bluetooth (direct, no receiver)
Affected area(s)
What happened?
The MX Anywhere 3S is paired and works as a normal macOS mouse, but OpenLogi usually shows it as offline or reports zero detected devices. Settings such as reverse scrolling do not take effect because the agent never establishes a usable inventory/control route.
The device is supported and permissions are not the blocker:
0xff43, usage0x0202.OpenLogiAgent.app.openlogi listtrace eventually completed the same 35-entry feature walk and correctly identified the mouse, including battery and BTLE transport.The failure occurs when any one request during the feature-table walk receives no response. The failed slot varies between attempts.
enumerate_features()returns an error,probe_features()discards all features already read, andprobe_direct()then classifies the Bluetooth-direct mouse as a likely receiver secondary interface:The persistent agent then evicts/reopens the channel and repeats this indefinitely. The GUI retains a saved device record but marks it offline, or reports no device.
This makes a recoverable dropped BLE HID++ response become a complete device-discovery failure. It also explains why killing/restarting the agent or rescanning sometimes appears to fix the device: a later attempt may happen to complete every request.
Expected behavior
A single feature-table request timeout should be retried or tolerated. A Bluetooth-direct node that was opened successfully and returned valid HID++ responses should not be discarded and described as a receiver secondary interface solely because one later feature-table request timed out.
At minimum, OpenLogi should retain/replay the last known direct-device identity and capabilities while retrying the failed request.
Steps to reproduce
Pair an MX Anywhere 3S directly through macOS Bluetooth.
Fully quit/uninstall Logi Options+ and ensure no competing Logitech process is running.
Grant Accessibility and Input Monitoring to OpenLogi Agent.
Quit the GUI and agent.
Run:
Repeat if necessary. Some attempts read part of the 35-entry feature table and then time out; another attempt may complete.
Start
openlogi-agentwithOPENLOGI_LOG=trace. In this environment it repeatedly aborts the feature walk, evicts the channel, and never logsBT-direct / wired device recognised.Reopen the GUI. The saved MX Anywhere 3S remains offline and device settings do not take effect.
Diagnostics report
openlogi listoutputAn exclusive trace demonstrates that the exact same device can eventually be identified when one retry completes:
Earlier retries in that same process failed:
Detailed persistent-agent trace
The agent was launched alone (GUI closed), so this happens before gesture/control capture becomes active.
Attempt 1 reached slot 12, then timed out:
A later attempt failed immediately after slot 4:
TRACE hidpp::device: enumerating feature table index=255 count=35 TRACE hidpp::device: feature index=255 slot=1 id=0x0001 version=2 TRACE hidpp::device: feature index=255 slot=2 id=0x0003 version=4 TRACE hidpp::device: feature index=255 slot=3 id=0x0005 version=0 TRACE hidpp::device: feature index=255 slot=4 id=0x1d4b version=0 TRACE hidpp::channel: hidpp request dev=255 feat=1 func=17 # approximately five seconds TRACE hidpp::channel: hidpp no response dev=255 feat=1 error=Timeout DEBUG openlogi_hid::inventory::features: enumerate_features failed slot=255 error=Channel(Timeout)Another reached slot 20 before failing:
TRACE hidpp::device: feature index=255 slot=17 id=0x2251 version=0 TRACE hidpp::device: feature index=255 slot=18 id=0x00c3 version=0 TRACE hidpp::device: feature index=255 slot=19 id=0x1802 version=0 TRACE hidpp::device: feature index=255 slot=20 id=0x1803 version=0 TRACE hidpp::channel: hidpp request dev=255 feat=1 func=17 # approximately five seconds TRACE hidpp::channel: hidpp no response dev=255 feat=1 error=TimeoutObserved failure points across consecutive attempts included immediately after slots 0, 1, 4, 6, 9, 10, 12, 15, 16, and 20. That varying position argues against one unsupported feature ID.
Some channel writes also returned:
Apple's local IOKit
IOReturn.hdefines0xE00002D6askIOReturnTimeout(I/O Timeout), notkIOReturnNotPermitted.After two failed ticks:
This cycle repeated for more than a minute without producing:
Therefore the initial failure is reproducible without GUI/agent contention and before the gesture capture watcher can open a second channel.
Relevant v0.6.22 code path
crates/openlogi-hidpp/src/device.rs:147-149A single failed request aborts the full walk; there is no per-entry retry/backoff.
crates/openlogi-hid/src/inventory/features.rs:91-101Any
enumerate_features()error immediately returnsProbedFeatures::default(), discarding successfully read entries.crates/openlogi-hid/src/inventory/probe.rs:436-455Empty capabilities/battery make
is_peripheralfalse, so the direct BLE mouse is logged as a likely receiver secondary interface and returned with no inventory.Possible fix direction
Related: #159, #218, #254, #322, #348, and #454.
macOS permissions