Oct 19, 4:11pm

This looks like the known BlueZ 5.87 regression, not a config problem on your side.

What's happening

  • BlueZ 5.87 shipped commit 912f5efb0dd9 ("a2dp: Fix handling of codec capability storage"), which broke the loop that parses cached remote SEP capabilities: i starts at 0 but the loop requires i >= 2, so the body never runs and every cached endpoint is discarded — producing exactly your load_remote_sep() Unable to load LastUsed: rseid 1 not found message (patch discussion).
  • With no cached remote SEPs, AVDTP discovery on every reconnect races with the headset's own Set Configuration, so the stream never gets properly established, and PipeWire/WirePlumber's transport open fails — the Failure in Bluetooth audio transport ... /sep1/fd1 and node running -> error lines you see. The same pattern is reported for Sony WH-1000XM5 and other A2DP devices on bluez 5.87-2 (bluez#2575, bluez#2321, bluez#2553).
  • The Unable to load LastUsed line even appears on connections that work, so it's easy to dismiss — but it's the root-cause marker (bluez#2321).

The fix is upstream commit b7d71e5 ("a2dp: Fix loading of remote SEP from cache"), merged on 2026-07-10 into BlueZ master (fix commit) — i.e. it should land in BlueZ 5.88.

What to do on Debian/sid

  1. Check your version: dpkg -l bluez — if it's 5.87.x, you're affected. The clean fix is simply waiting for / installing bluez ≥ 5.88 once it hits unstable (or temporarily pinning bluez from experimental if 5.88 is there).
  2. As a workaround, forcing a full re-discovery often restores audio:
    • bluetoothctl disconnect 38:18:4C:4B:AC:93 then connect again (rebuilding the in-memory SEP state is what fixes it, not the on-disk cache — deleting /var/lib/bluetooth/<adapter>/cache/<device> alone doesn't reliably help) (bluez#2553);
    • if the state is really stuck, sudo systemctl restart bluetooth plus systemctl --user restart wireplumber pipewire pipewire-pulse.
  3. If WirePlumber's automatic A2DP↔HFP profile switching keeps flipping the headset into a broken state, you can disable it with bluetooth.autoswitch-to-headset-profile = false in your WirePlumber bluetooth config (WirePlumber docs).

The SCO packet for unknown connection handle 513 kernel line is a side effect of the failed HFP/eSCO session teardown, and the firejail/firefox AppArmor denial is unrelated noise.

Oct 19, 4:11pm
Oct 20, 9:07am

You're right — those lines are the normal signature of the BlueZ 5.87 cache regression and they appear even on healthy connections, so they're noise as far as diagnosing this goes. What likely differed with Webex is not the Bluetooth stack but how the application reacts to the profile switch.

Why the browser test worked but Webex didn't

Both sessions show the same "background" errors, but there are two meaningful differences between the WebRTC test page and the Webex app:

  1. The A2DP↔HFP profile switch itself. Requesting the microphone forces WirePlumber to switch the WH-1000XM3 from A2DP to the hands-free profile mid-session, which destroys and recreates the sink/source nodes. That's exactly what your bluez_input/bluez_output nodes going to error at 16:58:55 trace — a switch storm, likely several in a row as Webex opens/closes streams. The switch is inherently disruptive, and browsers handle it well because WebRTC continuously renegotiates with the PulseAudio server and re-attaches to whatever node replaces the old one.

  2. Webex's media engine doesn't recover from it. The proprietary Cisco media stack typically opens its audio devices once at meeting start and holds them. When the node it captured is destroyed by the profile switch, it doesn't cleanly re-open the new device, and you get the errors you logged at 16:58:55 — port_set_param(Format) failed: Invalid argument and pw.link: one of the nodes is in error — which are the signature of a client (Webex) still trying to negotiate a format on a node that just died. This class of "app holds a stale device handle after a Bluetooth profile change" failure is a recurring theme with non-WebRTC conferencing apps (ArchWiki, Gentoo wiki); in your successful gum_test run, the same node errors appear but Firefox simply re-attaches and everything continues.

Also note the Sony quirk: the WH-1000XM3 is specifically known to misbehave when both hsp_ag and hfp_ag roles are enabled, which is why WirePlumber disables them by default (WirePlumber docs) — extra role negotiation during a Webex call can worsen the switch behavior.

What to try for the next Webex meeting

  • Pre-switch manually before joining: put the headset in HFP/hands-free profile in pactl/pavucontrol (or KDE/GNOME sound settings) before starting the meeting, so Webex never triggers a mid-session switch. If you don't need the mic, disable it in Webex so it stays on A2DP.
  • Or disable the automatic switch so Webex can't flip the profile mid-call: set bluetooth.autoswitch-to-headset-profile = false in monitor.bluez.properties (WirePlumber docs).
  • Retry/rejoin after it breaks: since Webex binds devices at meeting start, leaving and rejoining (with the correct profile already active) often fixes it without touching anything else.
  • If it breaks again, capture pw-dump | grep -A5 bluez and pactl list cards during the meeting to confirm which node Webex is stuck on.

Upgrading to bluez ≥ 5.88 (with the b7d71e5 cache fix) is still worthwhile — the regression forces a full AVDTP discovery on every reconnect instead of the cached path, which widens the race window during these profile switches (bluez#2575) — but the immediate Webex failure is most plausibly the app's stale device handle, not that log line.

Oct 20, 9:07am
Oct 20, 9:17am

Good corrections — that invalidates my profile-switch theory, and it's worth revising honestly. With the laptop mic and the headset staying on A2DP, the headset is purely an A2DP output device, and there was no profile switch involved in either session.

Revised read of the logs

Notice that your working Mozilla session shows essentially the same failure signature as the Webex one: bluez_output ... running -> error, Failure in Bluetooth audio transport, followed a second later by sep1/fd1: fd(34) ready. That sequence means the first attempt to acquire the A2DP transport failed, then a retry succeeded. This is exactly the behavior expected from the BlueZ 5.87 SEP-cache regression: without the cache, the incoming AVDTP setup races with bluetoothd's discovery, so the first Acquire of the transport can fail, and it only works on a later attempt (bluez#2321, bluez#2575).

So the difference between the two sessions is most likely not whether the error occurred, but what the application did with it:

  • Firefox's WebRTC audio path re-opens the sink and re-negotiates when the node errors out, so it recovers transparently — the transport then comes up (fd ready) and playback works.
  • Webex opened the headset sink at a specific moment (meeting start / audio connect), got an error state on the node, and apparently did not retry — leaving you with no audio. The port_set_param(Format) failed: Invalid argument and one of the nodes is in error lines in your Oct 19 log are the client-side symptom of Webex (via its media engine) still negotiating against a node that had just entered the error state.

Regarding WebRTC: the Webex web app uses WebRTC in the browser, but the desktop app runs Cisco's own media engine, which handles PulseAudio/PipeWire device errors and re-negotiation differently — historically this has been a source of Linux-specific audio bugs, e.g. the old Chrome PulseAudio race that showed up most often with WebEx (zmatt's analysis, Mozilla bug 1636480).

What to test next time

  1. Timing: check whether the Failure in Bluetooth audio transport lines in journalctl line up with the exact moment you joined the meeting / audio connected. If yes, this confirms the "first Acquire fails, Webex doesn't retry" picture.
  2. Pre-open the transport before joining: play a few seconds of audio to the headset just before the meeting starts, so the transport is already up (in the fd ready state) when Webex connects. If that fixes it, it's strong confirmation — and a usable workaround.
  3. Try Webex in the browser (web.webex.com) with the headset — if the WebRTC path works where the desktop app fails, that isolates it to Webex's media engine.
  4. Check whether Webex is firejailed — I noticed Firefox runs under firejail on your system (the AppArmor userns_create denial). If Webex also runs under a firejail profile, its access to the PipeWire/PulseAudio socket can be degraded in ways that surface exactly as device-open failures. Compare firejail --list while it's running.

And the underlying fix is still upgrading bluez once ≥ 5.88 lands in unstable — removing the race makes the first transport acquire reliable, which should make the whole question moot regardless of how Webex handles errors (fix commit).

Oct 20, 9:17am
Oct 20, 9:21am

OK, that removes the app-vs-browser distinction too — both were Firefox, both WebRTC, same mic, same A2DP output. At that point the honest answer is: the log line alone doesn't explain it, and the difference between the two sessions must lie elsewhere. But there is one meaningful difference between your two log captures that I glossed over:

Oct 19 vs Oct 20 logs are not the same pattern

  • Oct 20 (working test): one failure at 10:45:30, transport reacquired a second later (sep1/fd1: fd(34) ready), then apparently stable — a transient that the retry path absorbed.
  • Oct 19 (Webex failing): the transport kept failing — 16:56:29, then again 16:58:54, then 16:58:55 with multiple different node IDs (.0-94, .1-88, .1-79) and cascading client-side errors (port_set_param(Format) failed, one of the nodes is in error). That is a persistent, repeating failure loop over several minutes, not a one-shot race — and it matches your actual symptom that Webex audio wasn't working.

So the question becomes: why did the A2DP transport repeatedly fail on Oct 19 while it held on Oct 20? Candidates, roughly in order of plausibility:

  1. Headset multipoint. The WH-1000XM3 supports connecting to two devices. If it was also connected to (or fighting with) your phone on Oct 19, incoming traffic from the other host can repeatedly tear down or stall the A2DP stream. The stray SCO packet for unknown connection handle 513 lines are suspicious — they suggest an HFP/eSCO session existing (or half-tearing-down) that the kernel didn't expect, which fits the headset negotiating with another host or profile at the same time.
  2. Link/adapter conditions at that moment: 2.4 GHz congestion, headset distance, USB3 interference, or the adapter's firmware wedging — all produce exactly this "transport errors on and off for minutes" pattern.
  3. Timing vs. streaming state. On Oct 20 the transport may have been opened while you had continuous playback to the headset, keeping the stream alive during the test; on Oct 19 the Webex meeting may have had enough silence/pauses to let the transport suspend and hit the buggy re-acquire path (BlueZ 5.87's lost SEP cache makes every re-acquire a full AVDTP rediscovery race) repeatedly.

What would actually settle it

  • Reproduce: join a Webex meeting (or any long WebRTC call, e.g. a Jitsi room) now and watch journalctl -kf and pw-top. If a browser WebRTC call fails where gum_test succeeds, the trigger is the call pattern (long-lived stream with idle gaps), not Webex specifically.
  • During a failure, check bluetoothctl info 38:18:4C:4B:AC:93 — is Headset Gateway/Handsfree connected when it shouldn't be? And is the headset possibly still paired-and-connected to your phone at the same time?
  • Try forcing a robust codec (SBC instead of LDAC) via WirePlumber's bluez5.codecs setting — LDAC's higher bitrate is much less tolerant of a marginal link, and switching to SBC is a quick way to distinguish "link quality" from "stack bug."

And do upgrade bluez once ≥ 5.88 reaches unstable: it won't fix a multipoint or RF problem, but it removes the rediscovery race that turns every suspend/resume cycle into a coin flip, which is likely why the same headset works some days and not others.

Oct 20, 9:21am
Oct 20, 9:29am

That mSBC detail actually cracks the case — it's the missing clue, and it explains the whole Oct 19 log.

What the codec tells us

mSBC is an HFP-only codec; it doesn't exist in A2DP. If pavucontrol shows the headset's codec as mSBC "by default", then when your headset connects, WirePlumber puts it in the headset (HFP) profile — that's the default resting state. It then switches to A2DP/SBC when actual media playback starts. (pavucontrol shows the codec of the active profile, so "mSBC by default, SBC during the test" means a profile switch HFP→A2DP happened when the test began — the profile was not continuously A2DP.)

Now re-read the Oct 19 log with this in mind:

  • bluez_input.38_18_4C_4B_AC_93... nodes keep going to error. A bluez_input node only exists in the headset profile — it's the headset's microphone. So during the Webex meeting, WirePlumber was switching the XM3 into HFP, even though you intended to use the laptop mic.
  • The SCO packet for unknown connection handle 513 kernel errors are the signature of a failed HFP SCO/eSCO audio connection — the controller-level voice link that carries HFP audio never established properly.

Why Webex and not gum_test

WirePlumber auto-switches a Bluetooth device to the headset profile when an audio stream with the Communication media role appears (this is the bluetooth.autoswitch-to-headset-profile / media-role.use-headset-profile policy). Conferencing apps tag their audio as Communication; Firefox's WebRTC audio in a Webex meeting carries that role too. Your gum_test page's streams apparently didn't (or Firefox tagged them differently), so the headset stayed/switched to A2DP and worked.

So the Oct 19 sequence was: Webex starts → stream with Communication role → WirePlumber flips the XM3 to HFP → the SCO/mSBC voice link fails on your adapter (the kernel errors) → nodes error out → WirePlumber may flip back, Webex retriggers, flip-flop — the repeated transport failures and the multiple node IDs (.1-79, .1-88, .0-94) are successive profile switches, not a broken A2DP stream.

Fix

Since you don't want the headset mic anyway, just stop WirePlumber from ever switching it. Create /home/user/.../wireplumber.conf.d/99-no-autoswitch.conf (in ~/.config/wireplumber/ or /etc/wireplumber/):

text
wireplumber.components = [
  { name = libwireplumber/module-standard-audio-libs, type = module }
]
wireplumber.settings = {
  bluetooth.autoswitch-to-headset-profile = false
}

(The exact file depends on your WirePlumber version — on 0.5.x it's a wireplumber.conf.d snippet with bluetooth.autoswitch-to-headset-profile = false under wireplumber.settings; check /usr/share/wireplumber/wireplumber.conf for the current key name and just override the value.) Then systemctl --user restart wireplumber, and in a Webex meeting explicitly select the laptop mic + headset output (the A2DP sink) in Firefox's device picker / pavucontrol. The headset will then stay on A2DP/SBC for the whole call.

Alternatively, if you ever do want the headset mic, the underlying mSBC/SCO failure is worth fixing — but since the laptop mic is your intended input, disabling the auto-switch is the clean solution. The load_remote_sep messages and the BlueZ 5.87 upgrade remain cosmetic/unrelated to this particular failure.

Oct 20, 9:29am
Oct 20, 9:35am

The mSBC/SCO failure is actually a known, currently-active bug cluster, and with a WH-1000XM3 + BlueZ 5.87 you're right in the middle of it. Two distinct upstream defects match your logs:

1. BlueZ 5.87 + Sony headsets: the second profile switch breaks

BlueZ issue #2575 reports exactly your scenario on a Sony WH-1000XM5 with bluez 5.87 and PipeWire's native HFP backend: the first A2DP→HFP switch works, but on a subsequent switch on the same connection the new HFP nodes error out, the SCO transport fails ~12 s later, and switching back to A2DP then fails in bluetoothd (SEP in bad state for resume → transport Acquire denied) until the headset is fully disconnected and reconnected (bluez#2575). That's your repeated bluez_input/bluez_output error loops with different node IDs, and it also ties back to the load_remote_sep cache regression, which makes the return to A2DP unreliable. Partially mitigated by the b7d71e5 fix (bluez ≥ 5.88).

2. PipeWire native HFP backend: AT+BCC during eSCO setup kills the transport

A second, complementary bug: with PipeWire's backend-native, some headsets send AT+BCC (codec change request) while the eSCO link is being set up; PipeWire answers with the already-active codec but still tears down and re-creates the transport, closing the SCO socket. The result is that the mic works once per connection and every later A2DP↔HFP switch produces a dead mic/silent transport until reconnect (fix repo with explanation, upstream reported as pipewire#5506). A patch ("don't free a transport with a pending SCO connect") is proposed upstream — check whether your PipeWire version in unstable includes it.

The SCO packet for unknown connection handle 513 (0x0201) kernel lines fit this picture: stale/aborted SCO traffic arriving after the socket was closed — a symptom of the teardown race, not an independent fault. (Unless your adapter is a MediaTek MT7921/7925 USB dongle, where a separate firmware bug delivers eSCO frames on a bogus handle with zero payload and makes HFP unusable outright (MT7925 analysis, bluez#2545) — check lsusb / btmgmt info to rule that out; Intel/Realtek adapters are fine in that respect.)

Practical advice, keeping the switch enabled

  1. Upgrade path is the real fix: bluez ≥ 5.88 (once it lands in sid) plus a PipeWire with the AT+BCC/pending-SCO fix. Track pipewire#5506 and bluez#2575; both are recent and patches are in flight.
  2. Workaround for now: when the mic/voice profile breaks mid-session, don't toggle profiles — that just poisons it further. Do a full bluetoothctl disconnect 38:18:4C:4B:AC:93 && bluetoothctl connect ..., which reliably resets both the AVDTP and SCO state (bluez#2575, bluez#2545).
  3. Pre-empt it: before a meeting where you'll want the headset mic, switch the profile to HFP manually first and let it settle, then join — the failure mode is specifically the mid-session re-switch, especially the second one.
  4. If you ever need a reliable fallback, forcing CVSD instead of mSBC (bluez5.enable-msbc = false) sidesteps eSCO entirely on some adapters, at a large quality cost — worth knowing as a diagnostic, not a recommendation.

One thing worth pinning down: does your mic fail on the first HFP activation after connecting, or only on later switches? That distinguishes bug #2 (first use OK, later switches dead) from a pure adapter/firmware problem, and it's exactly the detail the upstream reports are being triaged on.

Oct 20, 9:35am

This is a shared conversation and may include unverified content that doesn't reflect Mistral AI's views. Login to resume chatting.

Vibe can make mistakes. Check answers. Learn more