Bluetooth Pairs, No Audio on Linux – Fix in 2 Minutes
Your Bluetooth device pairs but no audio plays. The culprit is almost always PulseAudio or PipeWire grabbing the wrong profile. Here's the real fix.
You're Not Crazy – This Happens All the Time
Your Bluetooth headphones or speaker pairs fine, shows up as connected, but audio stays on the laptop speakers. Or you get choppy, robotic sound. I've seen this on Ubuntu 22.04, Fedora 38, Debian 12 – you name it. The fix is simple once you know where to look.
The Real Fix: Force the A2DP Profile
The problem is almost always PulseAudio (or PipeWire) picking the wrong profile. Bluetooth has a bunch of profiles – HSP/HFP for phone calls (crappy mono audio) and A2DP for high-quality stereo music. Linux often defaults to HSP. Here's how to fix it right now.
If You're on PulseAudio
Open a terminal. First, check which sink your device is using:
pactl list sinks short | grep blue
You'll see something like bluez_sink.XX_XX_XX_XX_XX_XX.a2dp_sink or bluez_sink.XX_XX_XX_XX_XX_XX.headset_head_unit. If it says headset_head_unit, that's your problem. Run this command to switch to A2DP:
pactl set-card-profile $(pactl list cards short | grep -i blue | awk '{print $1}') a2dp_sink
If that doesn't work (some older PulseAudio versions need the full profile name), try:
pactl set-card-profile bluez_card.XX_XX_XX_XX_XX_XX a2dp_sink
Replace XX_XX_XX_XX_XX_XX with your device's MAC address (you can get it from bluetoothctl devices).
If You're on PipeWire (Fedora 34+, Ubuntu 22.10+ default)
PipeWire still uses similar PulseAudio commands for Bluetooth. Same logic applies. Run:
pactl list cards | grep -A 20 'bluez'
Look for a2dp-sink in the profiles section. Then set it:
pactl set-card-profile bluez_card.XX_XX_XX_XX_XX_XX a2dp-sink
Note the hyphen, not underscore – PipeWire uses a2dp-sink instead of a2dp_sink in some versions.
Why This Works
Bluetooth audio profiles are picky. The headset profile (HSP/HFP) uses 8 kHz mono audio – fine for phone calls, terrible for music. A2DP uses up to 48 kHz stereo. The problem is that many Bluetooth devices advertise all profiles, and PulseAudio picks the wrong one on connection. This happens a lot with headsets that have built-in microphones (like the Sony WH-1000XM series or AirPods). The device tells the system "I can do calls and music," and Linux defaults to the call profile because it's "simpler."
The pactl set-card-profile command forces the audio sink to use the high-quality profile. Your device will then stream properly. You'll hear the difference immediately.
Less Common Variations
Sometimes the profile switch doesn't stick, or audio still doesn't work. Here are the edge cases I've run into.
1. Auto-Switching Back to HSP
If PulseAudio flips back to headset profile after a few seconds, the problem is typically the headset's microphone. PulseAudio enables HSP when it detects a microphone input. The fix: disable the microphone source for that device.
pactl set-source-mute bluez_source.XX_XX_XX_XX_XX_XX.headset_head_unit 1
Or better, blacklist the microphone source entirely by adding this to /etc/pulse/default.pa:
load-module module-switch-on-connect blacklist="bluez_source.XX_XX_XX_XX_XX_XX"
2. Robotic or Crackling Audio
That's usually a codec problem. Some older Bluetooth adapters (especially USB dongles) can't handle high-bitrate A2DP. Drop the quality:
pactl set-card-profile bluez_card.XX_XX_XX_XX_XX_XX a2dp_sink_sbc
Or for PipeWire, install libfdk-aac or libldac to get better codec support. On Ubuntu: sudo apt install libfdk-aac2. Then restart PipeWire: systemctl --user restart pipewire.
3. PipeWire Switching Automatically
PipeWire 0.3.32+ has a feature that auto-switches profiles based on whether an input (microphone) is active. If you have a microphone on your headset and you launch an app that requests mic access (like Discord or Zoom), it flips to HSP. Workaround: disable auto-switching in /etc/pipewire/media-session.d/media-session.conf by setting bluez5.auto_switch to false.
Prevention – Make It Stick
Don't want to run that command every time you connect? Good. I don't either.
For PulseAudio: Create a file /etc/pulse/default.pa.d/bluetooth-fix.pa with one line:
set-card-profile bluez_card.XX_XX_XX_XX_XX_XX a2dp_sink
Replace the MAC with your device's MAC. PulseAudio reads all files in that directory on start.
For PipeWire: Add a udev rule or use wireplumber policy. Easiest: install pipewire-zeroconf and set the default node property. In /etc/pipewire/media-session.d/media-session.conf, add:
bluez5.default.rate = 48000
bluez5.default.channels = 2
This forces high-quality output by default.
I've also had luck with autoconnect in Bluetooth settings – on GNOME, disable "Connect automatically" and reconnect manually after setting the profile. Some firmware just doesn't like automated profile negotiation.
If you're still stuck after all this, check your Bluetooth adapter's chipset. Cheap CSR 4.0 dongles are notorious for dropping high-bandwidth streams. Replace it with an Intel AX200 or a good USB adapter based on Realtek 8822CU – they're $15 and solve half your issues.
Was this solution helpful?