Logitech Master 3s - Configuration breaks after reconnect

Just quickly:
I am a UIUX designer and a big UI customisation nerd, I have used BTT for more than a decade as my main UI customisation tool. Whilst everyone else on the planet was hating the touch bar, I loved it because of BTT.
Bravo Andreas, I probably wouldn't have gotten into this profession without your software.

Note on Logitech:
I have always loved Logi hardware and loathed their configuration software (Logi Options and Options+) for the same reasons most probably do, mainly because of their custom HID signals not being picked up in other software. In order to customise the i/o, I have set mouse buttons set to trigger various Function keys in LogiOptions+ and then set all kinds of triggers for the function keys in BTT (Keyboard shortcuts and Key Sequences). Also needed Karabiner-elements for some customisation. This is a mess to set up and troubleshoot, but that's what i've been stably running for a while.

I was very excited to see that BTT is supporting the Logi HID protocol, and that it meant deleting LogiOptions+. I set aside a few hours to delete LogiOptions+ and Karabiner-Elements and set everything up in BTT. Aside from a bit of JS to replicate the logi gestures, everything has worked beautifully. The only problem i've had is the configuration and all of my customisations & macros breaking on a pretty regular basis.
I have then spent quite a bit of time trying to pinpoint the issues. I've created instrumentation and tested a bunch of hypotheses. I have created JS pollers to detect issues and repair scripts to fix them. However now i'm starting to get crash loop when running the pollers and the repair scripts, i think there is an issue when I manually call Logitech JS functions in a certain way. I am unsure whether the issue is in my implementation or with the BTT Logitech Bridge.

At this point, it's either revert to using LogiOptions + Karabiner-Elements + BTT, or I ask the expert for help. I will list out the details below. I have also attached a TEST_LOG, apologies there is a lot there.
TEST_LOG_FOR_DEBUG_REPORT.pdf (738.0 KB)

I figure instead of giving you all of the JS i used, i'll give you this and let you request what you need from me.

P.S - I prefer to use the bolt receiver rather than bluetooth - when 3 or more bluetooth devices are connected to this mac, I get bandwidth issues with my BT headphones.


Bug Summary

BetterTouchTool (BTT) intermittently fails to apply Logitech MX Master 3S configuration after reconnect events (sleep/wake, Easy-Switch away/back, cold boot/login). When this happens, diverted button macros stop firing and/or DPI drops to 1000 (cursor ~4x slower). The issue often does not self-recover even after 10+ minutes and is reliably fixed by restarting BTT.

Environment

  • macOS: Sequoia 15.6.1
  • Device: Logitech MX Master 3S via Logi Bolt receiver
  • Keyboard: MX Keys via Bluetooth
  • Conflicting software:
  • Logitech Options+ - uninstalled
  • Karabiner Elements - uninstalled
  • BTT: Version 6.051 - Logitech HID++ 2.0 support (Also a few previous versions, i've been updating to alpha every couple of days)

Symptoms

  • BTT macros do not fire for diverted buttons.
  • Pointer speed/DPI becomes unusably slow (reads as 1000 vs target 4000).
  • Some failures self-recover after ~8-16s; others do not recover without BTT restart.
  • Restarting BTT restores functionality; this is needed multiple times per day.

Steps to reproduce

Scenario A: Easy-Switch away/back

  1. Switch mouse to another computer via Easy-Switch.
  2. Wait 5-15 seconds.
  3. Switch back to this Mac.

Scenario B: Sleep/Wake

  1. Put Mac to sleep.
  2. Wait ~15-20 seconds.
  3. Wake Mac.
  4. Repeat multiple cycles (failures observed after 2-4 cycles).

Expected

  • BTT Logitech configuration (DPI + diverted buttons/macros) should be applied immediately and reliably after reconnect/switch-back.

Actual

  • BTT Logitech configuration intermittently fails to apply.
  • Macros stop firing for diverted buttons.
  • DPI sets to 1000, cursor moves ~4x slower.
  • Failures can persist for minutes with no recovery.
  • Restarting BTT restores correct behaviour.

Instrumentation and testing performed

Created BTT triggers (Named Triggers, Run Real JavaScript):

  • LOGI_POLL
  • LOGI_SNAPSHOT
  • LOGI_MARK
  • LOGI_EVENT_PROBE
  • Diagnostics:
    • LOGI_DIAGNOSTIC
    • LOGI_CHECK_MACROS

Trigger wiring in BTT:

  • LOGI_POLL attached to "Trigger On Script Output Change" (2s interval)
    • later version of the poller was refactored and attached to Time Based trigger (2s)
  • LOGI_MARK attached to a keyboard shortcut (manual timeline markers)
  • LOGI_SNAPSHOT attached to a keyboard shortcut (on-demand snapshots)
  • LOGI_EVENT_PROBE attached to Button 6 (thumb button) for event routing checks

Logging:

  • Dual logging (direct file write + unified log) to logs/logi.log
  • Quiet-by-default state change logging
  • LaunchAgent logstream.sh for continuous collection

Tests executed:

  • Easy-Switch event routing tests (multiple runs; success + failure cases)
  • Easy-Switch cursor/macro timing tests (simple vs gesture macros)
  • Sleep/Wake cycles with reproduction of IOHIDDeviceSetReport failed
  • BTT restart recovery test
  • BTT device log collection in both GOOD and FAILURE states
  • API instrumentation with retry logic + stability checks

Established findings

  1. Multiple distinct failure modes

    • Easy-Switch failure mode:
      • HID++ error: 0x04 appears when the device is switched away.
      • On return, API reports ready (btnOk=true) but DPI often shows transient values (1000 -> 4000) and sometimes gets stuck at 1000.
      • Cursor moves slowly when DPI is stuck at 1000.
    • Sleep/Wake failure mode:
      • IOHIDDeviceSetReport failed error appears.
      • Macros can fully stop working even when cursor speed is normal.
      • Error can persist indefinitely until BTT is restarted.
    • Combined mode:
      • Sleep/Wake with IOHIDDeviceSetReport failed plus slow cursor (DPI issues).
  2. Event routing is not always the blocker

    • Event probe triggers (LOGI_EVENT_PROBE) show that button events can reach BTT before DPI stabilizes in successful recoveries.
    • Therefore: "events never reach BTT during recovery" is refuted in successful cases.
  3. DPI + button diversion are timing-sensitive

    • logitech_set_dpi() can timeout during failure states.
    • logitech_divert_button() can timeout during unstable recovery.
    • Retry with exponential backoff succeeds when the device stabilizes.
    • Stability checks are required before applying configuration.
  4. Device enumeration can fail during failure states

    • BTT "Gather Logitech Device Logs" restarts the Logitech manager.
    • In a failure state (DPI=1000), the manager restart often results in:
      • "No Logitech HID++ devices found"
      • "Device not found" / request timeouts
    • In a working state, enumeration succeeds and device details are fully captured.
  5. isReady and get_mode() are unreliable health signals

    • dev.isReady can remain true even when the mouse is switched away.
    • logitech_get_mode() often returns undefined even when working.
  6. Selective button failure pattern

    • Control ID mappings:
      • Button 3 -> 0x0053
      • Button 4 -> 0x0056
      • Button 6 -> 0x00C3
      • Button 7 -> 0x00C4
    • During failures, lower control IDs (0x0053, 0x0056) tend to work while higher IDs (0x00C3, 0x00C4) often fail.

Observed timing patterns

Easy-Switch (successful recoveries)

  • Device away: ~10-19s (HID++ 0x04)
  • API ready: immediate upon return
  • DPI recovery: ~5-10s (1000 -> 4000)
  • Simple macros: can work ~0.9s after return
  • Cursor speed recovery: ~9-15s after return

Easy-Switch (failed recoveries)

  • API ready, but DPI stuck at 1000; cursor stays slow for minutes.

Sleep/Wake

  • Failures observed after 2-4 cycles.
  • IOHIDDeviceSetReport failed persists; macros stop working.
  • Restarting BTT clears the error within ~10-13s.

Verified hypotheses

Confirmed:

  • HID++ 0x04 corresponds to device away in Easy-Switch.
  • DPI and button diversion timeouts occur during unstable recovery.
  • Retry with backoff succeeds after stability.
  • Enumeration can fail completely during failure states.

Refuted:

  • "Events never reach BTT during recovery."
  • isReady is sufficient to detect away/back transitions.

Workarounds

  • Restarting BTT restores full functionality and clears persistent failure states.

Thanks for the thorough assessment :slight_smile:
Could you try running this terminal command while BTT is quit - and then if the issue occurs again send me the logs from ~/Library/Application Support/BetterTouchTool/Logs ?

defaults write com.hegenberg.BetterTouchTool BTTLogitechDebugEnabled 2
(andreas@folivora.ai)

I guess it is related to being connected via the Bolt receiver because I'm mostly using my Logitech mouses via Bluetooth thus haven't tested that connection type as thoroughly yet.

Sent some logs through just now. I'll send more when the issue occurs in the wild.

Thanks Andreas

I think I found one issue in your logs! (Not sure if it is THE one, but it might be). Receivers with multiple devices connected to them are not being reinitialized correctly.

I have added an initial fix to 6.057 alpha which is uploading now

Yo nice man, I have tested that failure scenario and can't get it to break, looks good!

I have just emailed you some more logs from a persistent failure :call_me_hand:

Thank you man

I’m experiencing the same issue.

But my mouse is connected via Bluetooth. Whenever the computer wakes from sleep, the BTT mouse settings stop working. They only come back after I restart the app.

@f172 which version are you on? there have been many improvements with the recent alphas

Hi Andreas, my version is the last one.

The last one doesn't mean much in context of BTT because there are multiple different update channels :slight_smile: the last alpha? (6.074)

You’re right. I was on 6.070. I’ve now updated to the latest alpha (6.072). I’ll monitor it and report back.

Thank you very much, and congratulations on the great work.

in 6.075 I have an added some more additional after wake checks! (uploading now)