Pairing fails with error: BT_SECURITY_ERR_UNSPECIFIED

Hi,

I'm having a pairing issue with nRF54L15 (Zephyr-based project) and I’m not sure if this is a security configuration problem or something related to bonding persistence.

My application requires BT_SECURITY_L2 to accept pairing. However, in some cases pairing fails with:

BT_SECURITY_ERR_UNSPECIFIED (error 9)

From the logs it looks like the stack sometimes attempts to negotiate BT_SECURITY_L1, even though the application explicitly requires BT_SECURITY_L2.

What I’ve observed:

  • When using a Samsung S21 Ultra, the issue happens every time if I kill my app and then try to reconnect.
  • With other Android devices, the issue happens randomly - sometimes pairing works perfectly, other times it fails.
  • I’ve already checked some related tickets, but none of the suggested fixes solved my issue.

Questions:

  • Under what main conditions would a device attempt to negotiate BT_SECURITY_L1 instead of the required BT_SECURITY_L2?
  • Why does this issue appear to happen randomly? Is there a specific scenario or state transition that could trigger this behavior?

If useful, I can share relevant code. Thank you for any guidance.

  • Hi Joel,

    Would it be possible to attach the logs that you get?

    -Priyanka

  • The logs show the following behavior (tested on a Samsung S22 Ultra):

    - I initiate the connection and accept the pairing request successfully.
    - After closing and reopening the app, Android attempts to reconnect. However, the pairing then fails, and the firmware triggers an unpairing process.

    On other Android devices, the unpairing also happens, but in a more random or inconsistent manner.

    Logs:

    00> [00:01:11.973,443] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:12.975,605] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:12.975,842] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:13.978,004] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:13.978,239] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:14.682,053] <inf> bt_module: Connected
    00> [00:01:14.682,381] <inf> bt_module: Initial BLE parameteres: 
    00>            Connection interval is 36, 45 ms 
    00>            Connection peripheral latency is 0 
    00>            Connection supervision timeout  is 500, 5000 ms/n
    00> [00:01:14.683,207] <inf> bt_module: PHY update pending
    00> [00:01:14.683,395] <inf> bt_module: Connection parameters update pending
    00> [00:01:14.980,987] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:14.981,225] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:15.609,508] <inf> bt_module: PHY updated: peer=4B:56:A4:A4:01:87 (random), tx_phy=2, rx_phy=2
    00> [00:01:15.983,388] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:15.983,624] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:16.985,788] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:16.986,026] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:17.950,373] <inf> bt_module: Security OK: 4B:56:A4:A4:01:87 (random) level 2
    00> [00:01:17.988,183] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:17.988,421] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:18.400,043] <inf> bt_module: The CTS discovery procedure succeeded
    00> [00:01:18.400,263] <inf> sword_cts_client: Current Time characteristic found.
    00> [00:01:18.400,474] <inf> sword_cts_client: Local Time characteristic found.
    00> [00:01:18.580,090] <inf> grtc_module: RTC sync (UTC): 2026-02-11 14:18:52.352Z  offset=0 min  epoch_ms=1770819532352
    00> [00:01:18.670,484] <inf> bt_module: Local Time read: Timezone: 0, DST: 0
    00> [00:01:18.990,587] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:18.990,825] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:19.992,989] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:19.993,227] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:20.020,205] <inf> bt_module: Connection parameters updated: peer=A4:75:B9:5A:0F:11 (public), interval=200 ms, latency=3, timeout=4000 ms, MTU=498
    00> [00:01:20.995,387] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:20.995,625] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:21.997,780] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:21.998,018] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:23.000,180] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:23.000,415] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:24.002,549] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:24.002,783] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:25.004,916] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:25.005,151] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:26.007,285] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:26.007,519] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:27.009,652] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:27.009,887] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:27.175,807] <inf> bt_module: Disconnected (reason 19)
    00> [00:01:27.176,092] <inf> bt_module: Device name: MBAND Length: 5
    00> [00:01:27.176,580] <inf> bt_module: Added following peer to accept list: 11 f
    00> [00:01:27.177,827] <inf> bt_module: Advertising with Accept list...
    00> [00:01:28.012,020] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:28.012,254] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:29.014,390] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:29.014,626] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:30.016,789] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:30.017,025] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:31.019,156] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:31.019,393] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:32.021,524] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:32.021,760] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:33.023,893] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:33.024,129] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:34.026,293] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:34.026,530] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:34.216,440] <inf> bt_module: Connected
    00> [00:01:34.216,770] <inf> bt_module: Initial BLE parameteres: 
    00>            Connection interval is 36, 45 ms 
    00>            Connection peripheral latency is 0 
    00>            Connection supervision timeout  is 500, 5000 ms/n
    00> [00:01:34.217,576] <inf> bt_module: PHY update pending
    00> [00:01:34.217,760] <inf> bt_module: Connection parameters update pending
    00> [00:01:34.555,821] <wrn> bt_module: Security insufficient for A4:75:B9:5A:0F:11 (public) (level 1, err 9) — enforcing L2
    00> [00:01:34.559,146] <wrn> bt_module: bt_conn_disconnect failed (-128)
    00> [00:01:34.559,744] <inf> bt_module: Disconnected (reason 61)
    00> [00:01:34.560,035] <inf> bt_module: Device name: MBAND Length: 5
    00> [00:01:34.561,405] <inf> bt_module: Advertising with no Accept list...
    00> [00:01:35.028,692] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:35.028,928] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:36.031,061] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:36.031,295] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:37.033,429] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:37.033,665] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:38.035,829] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:38.036,066] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:39.038,197] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:39.038,433] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:39.589,482] <inf> bt_module: Connected
    00> [00:01:39.589,814] <inf> bt_module: Initial BLE parameteres: 
    00>            Connection interval is 36, 45 ms 
    00>            Connection peripheral latency is 0 
    00>            Connection supervision timeout  is 500, 5000 ms/n
    00> [00:01:39.590,642] <inf> bt_module: PHY update pending
    00> [00:01:39.590,829] <inf> bt_module: Connection parameters update pending
    00> [00:01:40.040,604] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:40.040,840] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:40.091,166] <inf> bt_module: Disconnected (reason 19)
    00> [00:01:40.091,449] <inf> bt_module: Device name: MBAND Length: 5
    00> [00:01:40.092,818] <inf> bt_module: Advertising with no Accept list...
    00> [00:01:41.042,996] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:41.043,232] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:41.804,127] <inf> grtc_module: Current Time in Unix (ms): 1770819555.576
    00> [00:01:42.045,365] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:42.045,602] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:43.047,765] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:43.048,001] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:44.050,133] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:44.050,369] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:45.052,533] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:45.052,769] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:46.054,932] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:46.055,169] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:47.057,333] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:47.057,569] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan
    00> [00:01:48.059,733] <inf> fuel_gauge: V: 4.204, I: 0.000, T: 4.20, 
    00> [00:01:48.059,969] <inf> fuel_gauge: SoC: 100.00, TTE: nan, TTF: nan


    The function below is responsible for triggering the unpairing process.

    static void on_security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
    {
        char addr[BT_ADDR_LE_STR_LEN];
        bt_addr_le_t peer = *bt_conn_get_dst(conn);
        bt_addr_le_to_str(&peer, addr, sizeof(addr));
    
        if (!err && level >= BT_SECURITY_L2) {
            // Event: BT Pairing success
            EVT_BT_EVENT_PAIRING_SUCCESS(addr);
            LOG_INF("Security OK: %s level %u", addr, level);
            /* Now it's safe to kick off CTS discovery, etc. */
            int ret = bt_gatt_dm_start(conn, BT_UUID_CTS, &discover_cb, NULL);
            if (ret) {
                LOG_WRN("CTS discovery start failed (%d)", ret);
            }
            return;
        }
    
        // Event: BT Pairing failed
        EVT_BT_EVENT_PAIRING_FAIL(addr, err);
        LOG_WRN("Security insufficient for %s (level %u, err %d) — enforcing L2", addr, level, err);
    
        /* If auth requirements failed or keys are bad, drop the bond with *this* peer */
        bt_unpair(BT_ID_DEFAULT, &peer);
    
        /* Disconnect with a sensible reason */
        int rc = bt_conn_disconnect(conn, BT_HCI_ERR_AUTH_FAIL);
        if (rc) {
            LOG_WRN("bt_conn_disconnect failed (%d)", rc);
        }
    }

  • Hi,

    From the snippet that you shared, I can see that you are calling bt_unpair() from inside security changed, which might not be a good idea. Please take a look at this:

    https://github.com/zephyrproject-rtos/zephyr/issues/59788 

    I would suggest you to just disconnect.

    -Priyanka

  • Hi again,

    I’ve implemented a few improvements to address the pairing issue:

    1- Unpair flow

    • I refactored the unpair logic so that it now only happens in the disconnected callback (previously it was triggered in on_security_changed).

    • I also moved the connection parameter update so that it is only triggered after successful pairing.

    2- KConfig Change

    • I added the following configuration:

      CONFIG_BT_GATT_AUTO_SEC_REQ=n
    • I believe this change fixed the issue I was seeing with my own phone.
      Previously, I could pair successfully the first time, but after closing and reopening the app, a new pairing attempt failed with:

      • “Security insufficient (level 1)” and error 9.

    After setting CONFIG_BT_GATT_AUTO_SEC_REQ=n, the pairing reliability improved significantly and the issue occurs much less frequently. However, I can still occasionally reproduce the same error 9.

    3- Pairing retry mechanism

    To mitigate this, I implemented a retry mechanism that allows up to three pairing attempts within 30 seconds before triggering an unpair. - I observed a significant improvement, after some tests, it never reached the thrid attempt.

    Please let me know if this approach aligns with the best practices, or if you would remommend a different strategy for handling pairing retries or security configuration.

    Thank you!

Related