BLE Connection Issues: Diagnosis and Resolution Guide
Fixing pairing failures, disconnections, and bonding problems
BLE Connection Issues: Diagnosis and Resolution Guide
Connection problems in Bluetooth Low Energy fall into three phases: discovery (device not visible), establishment (pairing/bonding failures), and maintenance (random disconnections). Each phase has distinct root causes and diagnostic approaches.
Phase 1: Discovery Failures
Device not appearing in scan results
Diagnostic flow:
1. Is the device advertising?
→ Check device-side LED/log: advertising state active?
→ nRF: sd_ble_gap_adv_start() returns NRF_SUCCESS?
2. Is the [advertising interval](/glossary/advertising-interval/) too long?
→ Minimum recommended: 100 ms (1000 ms = rare visibility)
→ Scanner window must overlap with adv event
3. Is the [advertising](/glossary/advertising/) non-connectable?
→ ADV_NONCONN_IND is filtered by many mobile OS scanners
→ Switch to ADV_IND for connectable + discoverable
4. Is there a [whitelist](/glossary/whitelist/) filtering active?
→ Disable filter temporarily for diagnostics
5. Is [LE Privacy](/glossary/le-privacy/) generating a rotating address?
→ Central must have bonded IRK to resolve; unregistered scanner sees unknown device
| Symptom | Root Cause | Fix |
|---|---|---|
| Invisible to phone, visible to sniffer | OS-level filter active | Check app's scan filter ATT">UUID |
| Intermittent visibility | advertising-interval/" class="glossary-term-link" data-term="Advertising interval" data-definition="Time between BLE advertising events." data-category="GAP & Advertising">Advertising interval > 500 ms | Reduce to 100–200 ms |
| Never visible after reboot | Advertising not auto-restarted | Add GAP disconnected → restart adv handler |
| Different address each scan | LE Privacy resolvable address | Bond device to resolve IRK |
Phase 2: Connection Establishment Failures
Pairing and bonding failures
Error codes and their meanings (Bluetooth Core Spec Vol 3, Part H):
| Error Code | Meaning | Typical Fix |
|---|---|---|
| 0x01 Passkey Entry Failed | User entered wrong PIN | Retry; check for UI timing issues |
| 0x02 OOB Not Available | OOB requested but not supported | Change pairing IO capabilities |
| 0x03 Authentication Requirements | Peer requires LESC, we only offer legacy | Enable LESC on both sides |
| 0x05 Pairing Not Supported | Device rejected pairing | Check security manager initialized |
| 0x08 Unspecified Reason | Catch-all | Check SMP logs; often MTU negotiation race |
| 0x0E Invalid Parameters | Malformed pairing request | Check IO caps and OOB flags |
iOS-specific: iOS caches bonding data. If the peripheral re-bonds with a new LTK (factory reset without informing iOS), iOS silently fails the encrypted connection. Resolution: un-pair the device in iOS Bluetooth settings before re-pairing.
Android-specific: Android 12+ requires BLUETOOTH_CONNECT and BLUETOOTH_SCAN runtime permissions. Missing permissions cause silent scan failures with no error callback.
Phase 3: Random Disconnections
Diagnostic flowchart:
Disconnection observed
│
┌────┴────┐
HCI code HCI code
0x08 0x13
(Timeout) (Remote terminate)
│ │
▼ ▼
Radio Application
issue layer issue
│ │
Check: Check:
- [RSSI](/glossary/rssi/) at time - Peripheral side
of disconnect disconnect reason
- 2.4 GHz - Supervision
interference timeout value
- [TX power](/glossary/tx-power/) - keepalive logic
| HCI Disconnect Reason | Code | Root Cause |
|---|---|---|
| Connection timeout | 0x08 | Radio link lost; supervision timeout expired |
| Remote user terminated | 0x13 | Peripheral app called disconnect |
| Remote device terminated (low resources) | 0x14 | Peripheral out of GATT buffers |
| Remote device terminated (power off) | 0x15 | Device lost power or rebooted |
| Unsupported LE feature | 0x1A | Feature negotiation mismatch |
MTU Negotiation Issues
Default ATT MTU is 23 bytes. Applications that assume larger MTU without negotiating it cause truncated writes:
# Android — MTU negotiation
gatt.requestMtu(247) # Call after onConnectionStateChange(CONNECTED)
# Wait for onMtuChanged callback before writing large characteristics
# iOS — MTU is auto-negotiated; read maximumWriteValueLength(for:)
let mtu = peripheral.maximumWriteValueLength(for: .withoutResponse)
If both peers support DLE (Data Length Extension), negotiate a larger LL PDU first (up to 251 bytes data), then negotiate ATT MTU to match. MTU > 23 bytes requires EATT or a connection parameter update that both peers support.
Use the GATT Browser to verify MTU negotiation and inspect the live attribute table during debugging. For throughput-specific issues, see BLE Throughput Optimization.
Frequently Asked Questions
Yes, our guides range from beginner introductions to advanced topics. Each guide indicates its difficulty level and prerequisites so you can find the right starting point.