You pair your headphones and hear nothing
You pair your Bluetooth headphones with Fedora. The status bar shows "Connected." You play a video. The audio comes out of the laptop speakers instead of the headphones. Or worse, the audio is tinny, mono, and sounds like a telephone call. You check the settings, toggle the device, and nothing changes. The connection is there, but the audio path is broken.
This happens because Bluetooth audio is not a single state. The protocol uses profiles to define how data moves. Fedora's sound server manages these profiles, and sometimes the switch between them fails or gets stuck. The fix usually involves forcing the correct profile or clearing a stale state in the Bluetooth daemon.
What's actually happening
Bluetooth audio relies on profiles. The protocol defines how data moves between the radio and the audio stack. A2DP (Advanced Audio Distribution Profile) sends high-quality stereo audio in one direction. HFP/HSP (Hands-Free/Headset Profile) opens a two-way channel for voice calls, which sacrifices quality to support the microphone.
Fedora uses PipeWire as the audio server. PipeWire manages the active profile for each device. When you connect a headset, PipeWire negotiates the profile with the Bluetooth daemon. If the negotiation fails, or if an application requests microphone access, PipeWire may switch the device to HFP. This switch can happen automatically. If you open a video conferencing app, the app requests microphone access. PipeWire detects this request and switches the Bluetooth profile from A2DP to HFP to support the mic. The audio quality drops instantly.
Close the conferencing app, and PipeWire should switch back to A2DP. If it doesn't, the switch is stuck. The device remains connected at the radio level, but the audio stream is routed to the wrong profile or blocked by a stale state.
Restart the services before you re-pair. The daemon state often clears on its own.
Check the services
PipeWire and the Bluetooth daemon must be running. If either service is stopped, audio routing fails silently. Verify the status of the core services.
systemctl status bluetooth pipewire pipewire-pulse
# Check if services are active. PipeWire handles audio, bluetooth handles the radio.
# Look for "active (running)" in the output.
If any service shows as inactive, start and enable them. The enable flag ensures they start on boot. The --now flag starts them immediately.
sudo systemctl enable --now bluetooth pipewire pipewire-pulse
# Enable ensures services start on boot. --now starts them immediately.
# pipewire-pulse provides the PulseAudio compatibility layer for older apps.
Run journalctl -xeu pipewire to see the last 50 lines of PipeWire logs with explanatory text. The -x flag adds context, and -e jumps to the end. This command is the standard way to inspect service logs on Fedora.
Force the audio profile
If the services are running but audio is missing or poor quality, the profile is likely wrong. You can force the switch to A2DP using the command line. PipeWire includes a PulseAudio compatibility layer, so pactl commands work even though PipeWire is the backend.
First, identify the card name for your device. The card name usually starts with bluez_card. followed by your MAC address with underscores.
pactl list cards short
# List audio cards. Find the line starting with bluez_card.
# Note the full name, e.g., bluez_card.XX_XX_XX_XX_XX_XX.
Set the profile to a2dp_sink. Replace the card name with the one you found.
pactl set-card-profile bluez_card.XX_XX_XX_XX_XX_XX a2dp_sink
# Force the card to use A2DP. Replace XX with your MAC address.
# This command overrides the current profile and switches to high fidelity.
If the command succeeds, audio should route to the headphones immediately. If you get an error about the profile not existing, your device may not support A2DP, or the Bluetooth stack hasn't fully enumerated the capabilities. Re-pair the device and try again.
Check the logs before you wipe the cache. You might save your paired devices.
The microphone trap
Applications request audio capabilities. If you open Discord, Zoom, or a browser tab with a video call, the app requests microphone access. PipeWire detects this request and switches the Bluetooth profile from A2DP to HFP to support the mic. This happens automatically. The audio quality drops instantly.
Close the conferencing app, and PipeWire should switch back to A2DP. If it doesn't, the switch is stuck. You can force the switch back using the pactl command above. Some applications hold the microphone lock even after closing. Check for background processes.
pactl list short modules
# List loaded modules. Look for module-switch-on-connect.
# This module handles automatic profile switching.
If the automatic switching is broken, you can reload the PipeWire configuration. This forces PipeWire to re-evaluate all active streams.
systemctl --user restart pipewire pipewire-pulse
# Restart PipeWire for the current user. This reloads the configuration.
# Active streams will reconnect. Audio may cut out briefly.
Verify the fix
Confirm the audio is routing correctly. Check the default sink and play a test sound.
pactl info | grep "Default Sink"
# Confirm the default sink points to your Bluetooth device.
# The output should show bluez_sink.XX_XX...
Play a system sound to verify audio routing.
paplay /usr/share/sounds/freedesktop/stereo/bell.oga
# Play a system sound to verify audio routing.
# This uses the PulseAudio compatibility layer to test playback.
If the sound plays through the headphones, the fix is complete. If the sound still plays through the speakers, the default sink is not set correctly. Set the default sink manually.
pactl set-default-sink bluez_sink.XX_XX_XX_XX_XX_XX
# Set the default sink to your Bluetooth device.
# Replace the sink name with the one from pactl list short sinks.
Common pitfalls
SELinux denials are rare for standard audio but possible. If you see errors in the logs, check for denials. Do not disable SELinux. Read the denial summary first.
sudo ausearch -m avc -ts recent | grep bluetooth
# Check for SELinux denials. Look for avc messages.
# If denials exist, check setroubleshoot for the one-line summary.
sudo journalctl -t setroubleshoot | tail -20
# Read the one-line summary. Do not disable SELinux yet.
# The summary often suggests a fix or a policy module.
A stale Bluetooth cache can cause persistent connection issues. Clearing the cache is a nuclear option. It removes all paired devices. Use this only when the daemon is stuck in a bad state and other fixes fail.
sudo rm -rf /var/lib/bluetooth/*
# Wipe the Bluetooth cache. This removes all paired devices.
# Use this only when the daemon is stuck in a bad state.
sudo systemctl restart bluetooth
# Restart the daemon to rebuild the database.
# You will need to re-pair all devices after this step.
Config files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. Edit /etc/. Never edit /usr/lib/. If you need to modify PipeWire configuration, copy the file to /etc/pipewire/ and edit the copy. The package manager will overwrite /usr/lib/ on updates.
Decision matrix
Use pactl set-card-profile when the device connects but audio is mono or routed to speakers. Use wpctl set-mute when the device is connected but the application volume is muted. Use bluetoothctl when you need to troubleshoot the pairing handshake itself. Use the cache wipe when the Bluetooth daemon reports errors and refuses to connect any device. Use dnf update when you are on an older kernel and your hardware requires newer firmware. Use journalctl -xeu when you need to inspect logs with explanatory context. Use systemctl --user restart pipewire when the audio server is stuck and a profile switch fails.
Trust the package manager. Manual file edits drift, snapshots stay.