Skip to content

[Bugfix] HCI response to disconnect of unknown ID should return success.#414

Merged
h2zero merged 1 commit intomasterfrom
bugfix/disconnect-unk-conn-id
Mar 28, 2026
Merged

[Bugfix] HCI response to disconnect of unknown ID should return success.#414
h2zero merged 1 commit intomasterfrom
bugfix/disconnect-unk-conn-id

Conversation

@h2zero
Copy link
Copy Markdown
Owner

@h2zero h2zero commented Mar 27, 2026

  • Update macro use to use conversion macros.

Summary by CodeRabbit

  • Bug Fixes
    • Improved detection and handling of missing PIN/key during secure connections to avoid false failures and unintended retries.
    • Treated an additional unknown-connection-ID termination response as non-fatal to prevent unnecessary error reports during disconnects.
    • Strengthened disconnect logic so real failures are correctly reported and logs include clearer, human-readable return codes for troubleshooting.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

Avast: standardized HCI error comparisons to BLE_HS_HCI_ERR(...), improved secureConnection() failure logs with human-readable strings, and tightened client/server disconnect handling to treat BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) as non-fatal in disconnect flows. Yarrr — concise and factual.

Changes

Cohort / File(s) Summary
Client HCI & Logging
src/NimBLEClient.cpp
Replaced base+offset HCI error comparisons with BLE_HS_HCI_ERR(...) for PIN-key-missing and conn-establishment; appended human-readable text from NimBLEUtils::returnCodeToString(...) to secureConnection() failure logs.
Client Disconnect & GAP Handling
src/NimBLEClient.cpp
Refactored disconnect(uint8_t) control flow to a switch handling rc==0, BLE_HS_ENOTCONN, BLE_HS_EALREADY, and BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) distinctly; updated handleGapEvent(...) encryption-change and retry branches to use BLE_HS_HCI_ERR(...) comparisons.
Server Disconnect Handling
src/NimBLEServer.cpp
Changed ble_gap_terminate return handling to treat BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) as non-fatal alongside 0, BLE_HS_ENOTCONN, and BLE_HS_EALREADY; other nonzero rc values now log error and return false.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

Yarrr, the HCI codes be right and clear,
Logs now whisper what we oughtta hear,
Unknown IDs cause no more fear,
Clients pull lines, servers steer,
Code patched fair — hoist the gear!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main bugfix: treating HCI disconnect responses for unknown connection IDs as success, which is the core change across both NimBLEClient and NimBLEServer.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bugfix/disconnect-unk-conn-id

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/NimBLEServer.cpp (1)

336-343: ⚠️ Potential issue | 🔴 Critical

Arrr, matey! The server be needin' cleanup when the controller says it knows not the connection.

Line 338 treats BLE_ERR_UNK_CONN_ID as success, but this be leavin' m_connectedPeers stale forever. When the controller reports it knows not the connection, there'll be no BLE_GAP_EVENT_DISCONNECT event comin' back to clean up yer bookkeeping. This means getConnectedCount(), getPeerDevices(), advertiseOnDisconnect, and the optional m_pClient->m_connHandle mirror stay stale. The client handles this right by settin' m_connStatus = DISCONNECTED on the spot (line 387); the server should do the same for m_connectedPeers before returnin' true.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NimBLEServer.cpp` around lines 336 - 343, NimBLEServer::disconnect
currently treats BLE_ERR_UNK_CONN_ID as a non-error and returns true without
cleaning up state; update disconnect(uint16_t connHandle, uint8_t reason) so
when ble_gap_terminate returns BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) you remove
the peer from m_connectedPeers (and perform the same per-peer cleanup you would
on BLE_GAP_EVENT_DISCONNECT) and update any mirror state (e.g., clear the peer's
m_connHandle, set its connection status to DISCONNECTED) before returning true
so getConnectedCount(), getPeerDevices(), advertiseOnDisconnect and related
logic reflect the disconnection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/NimBLEClient.cpp`:
- Around line 381-387: When ble_gap_terminate(...) returns BLE_ERR_UNK_CONN_ID
the code sets m_connStatus to DISCONNECTED without firing the
BLE_GAP_EVENT_DISCONNECT cleanup path, which can orphan clients when
deleteOnDisconnect is true; modify the post-terminate branch in NimBLEClient.cpp
(where BLE_ERR_UNK_CONN_ID is checked) to perform the same cleanup the GAP
disconnect handler does: detect the BLE_ERR_UNK_CONN_ID fast path, check
this->deleteOnDisconnect (and any relevant client flags), and invoke the same
cleanup sequence used by the GAP handler (or call the same helper used by that
handler) so delete-on-disconnect clients are freed immediately rather than
relying on an event. Ensure m_lastErr and m_connStatus are still set
appropriately and reuse existing cleanup logic to avoid duplication.

---

Outside diff comments:
In `@src/NimBLEServer.cpp`:
- Around line 336-343: NimBLEServer::disconnect currently treats
BLE_ERR_UNK_CONN_ID as a non-error and returns true without cleaning up state;
update disconnect(uint16_t connHandle, uint8_t reason) so when ble_gap_terminate
returns BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) you remove the peer from
m_connectedPeers (and perform the same per-peer cleanup you would on
BLE_GAP_EVENT_DISCONNECT) and update any mirror state (e.g., clear the peer's
m_connHandle, set its connection status to DISCONNECTED) before returning true
so getConnectedCount(), getPeerDevices(), advertiseOnDisconnect and related
logic reflect the disconnection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f943e235-e0ba-400f-b17a-09b83939accb

📥 Commits

Reviewing files that changed from the base of the PR and between afe1221 and 4e27a4f.

📒 Files selected for processing (2)
  • src/NimBLEClient.cpp
  • src/NimBLEServer.cpp

@h2zero h2zero force-pushed the bugfix/disconnect-unk-conn-id branch from 4e27a4f to 69cff9a Compare March 28, 2026 00:15
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/NimBLEClient.cpp (1)

381-387: ⚠️ Potential issue | 🟠 Major

Arrr, BLE_ERR_UNK_CONN_ID still leaves the client stuck in DISCONNECTING.

Line 381 now treats BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) as success, but Line 387 still maps it to DISCONNECTING. connect() only accepts DISCONNECTED at Lines 261-264, so this path now depends on a later GAP disconnect event to restore state. If that event never arrives for an unknown handle, the client stays wedged and reconnects get rejected. I’d treat BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) the same as BLE_HS_ENOTCONN in the local state machine.

🏴‍☠️ Minimal state fix
-    m_connStatus = rc == BLE_HS_ENOTCONN ? DISCONNECTED : DISCONNECTING;
+    m_connStatus = (rc == BLE_HS_ENOTCONN || rc == BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID))
+                       ? DISCONNECTED
+                       : DISCONNECTING;
#!/bin/bash
set -euo pipefail

client_file="$(fd -p 'NimBLEClient\.cpp$' src | head -n1)"
device_file="$(fd -p 'NimBLEDevice\.cpp$' src | head -n1)"

printf '\n== disconnect() success path ==\n'
sed -n '379,388p' "$client_file"

printf '\n== connect() precondition ==\n'
sed -n '255,265p' "$client_file"

printf '\n== all m_connStatus transitions in NimBLEClient.cpp ==\n'
rg -n -C2 'm_connStatus\s*=' "$client_file"

printf '\n== deleteClient / deleteOnDisconnect handling ==\n'
rg -n -C3 'deleteClient\s*\(|deleteOnDisconnect|deleteOnConnectFail' "$device_file" "$client_file"

Expected result: ye should see the unknown-connection success path landing in DISCONNECTING, connect() only allowing DISCONNECTED, and whether any separate delete-on-disconnect cleanup exists outside this branch.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NimBLEClient.cpp` around lines 381 - 387, The code treats
BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) as a non-error return from
ble_gap_terminate() but still sets m_connStatus to DISCONNECTING, which can
leave the client wedged; update the disconnect() success-path in
NimBLEClient.cpp so that when rc equals BLE_HS_ENOTCONN OR
BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) you set m_connStatus = DISCONNECTED (same as
the ENOTCONN case) and preserve m_lastErr handling, so connect() (which checks
for DISCONNECTED) will allow reconnection immediately.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/NimBLEClient.cpp`:
- Around line 381-387: The code treats BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) as a
non-error return from ble_gap_terminate() but still sets m_connStatus to
DISCONNECTING, which can leave the client wedged; update the disconnect()
success-path in NimBLEClient.cpp so that when rc equals BLE_HS_ENOTCONN OR
BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) you set m_connStatus = DISCONNECTED (same as
the ENOTCONN case) and preserve m_lastErr handling, so connect() (which checks
for DISCONNECTED) will allow reconnection immediately.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bd099957-ae8e-4cd2-891c-8f6cd7eaf01c

📥 Commits

Reviewing files that changed from the base of the PR and between 4e27a4f and 69cff9a.

📒 Files selected for processing (2)
  • src/NimBLEClient.cpp
  • src/NimBLEServer.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/NimBLEServer.cpp

@h2zero h2zero force-pushed the bugfix/disconnect-unk-conn-id branch 2 times, most recently from 5f8a1be to 0a0dbaa Compare March 28, 2026 16:45
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/NimBLEClient.cpp (1)

381-393: ⚠️ Potential issue | 🟠 Major

Arrr, this “success” branch still skips local disconnect finalization.

For BLE_HS_ENOTCONN and BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID), this returns true without clearing m_connHandle, setting m_connStatus = DISCONNECTED, or reaching the deleteOnDisconnect path that only runs in the BLE_GAP_EVENT_DISCONNECT handler on Lines 1036-1099. If no disconnect event follows those codes, a later connect() can still fail the guard on Lines 261-264 and self-delete never fires. Please route these cases through the same disconnect-finalization path instead of a bare success return.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NimBLEClient.cpp` around lines 381 - 393, The switch that treats
BLE_HS_ENOTCONN and BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) as a simple success must
instead perform the same local disconnect finalization as the
BLE_GAP_EVENT_DISCONNECT path: clear m_connHandle, set m_connStatus =
DISCONNECTED, and trigger the deleteOnDisconnect behavior (or call the same
helper used in the BLE_GAP_EVENT_DISCONNECT handler) before returning true;
update the case arms in the switch in NimBLEClient.cpp so they do not just
return true but run that finalization logic to avoid leaving the client in a
half-disconnected state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/NimBLEClient.cpp`:
- Around line 381-393: The switch that treats BLE_HS_ENOTCONN and
BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) as a simple success must instead perform the
same local disconnect finalization as the BLE_GAP_EVENT_DISCONNECT path: clear
m_connHandle, set m_connStatus = DISCONNECTED, and trigger the
deleteOnDisconnect behavior (or call the same helper used in the
BLE_GAP_EVENT_DISCONNECT handler) before returning true; update the case arms in
the switch in NimBLEClient.cpp so they do not just return true but run that
finalization logic to avoid leaving the client in a half-disconnected state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 19de5ead-c538-42bf-89b0-86310d5d6146

📥 Commits

Reviewing files that changed from the base of the PR and between 5f8a1be and 0a0dbaa.

📒 Files selected for processing (2)
  • src/NimBLEClient.cpp
  • src/NimBLEServer.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/NimBLEServer.cpp

@h2zero h2zero force-pushed the bugfix/disconnect-unk-conn-id branch from 0a0dbaa to 91251ea Compare March 28, 2026 17:28
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/NimBLEClient.cpp (1)

381-393: ⚠️ Potential issue | 🟠 Major

Arrr, the same ghost-client snag remains here.

On BLE_HS_ENOTCONN and BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID), we now return true without clearing m_connHandle / m_connStatus or applying the local finalization that the BLE_GAP_EVENT_DISCONNECT branch does. src/NimBLEDevice.cpp treats that true as “disconnect cleanup will finish later”, so this can strand a client in a phantom connected state and miss the self-delete path.

Ye can verify the missing local cleanup with:

#!/bin/bash
set -euo pipefail

printf '\n== NimBLEClient::disconnect fast path ==\n'
sed -n '379,394p' src/NimBLEClient.cpp

printf '\n== BLE_GAP_EVENT_DISCONNECT finalization ==\n'
sed -n '1087,1099p' src/NimBLEClient.cpp

printf '\n== deleteClient() path that trusts disconnect() ==\n'
rg -n -C4 'deleteOnDisconnect|disconnect\(' src/NimBLEDevice.cpp
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NimBLEClient.cpp` around lines 381 - 393, The fast-path in
NimBLEClient::disconnect currently returns true for BLE_HS_ENOTCONN and
BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) without performing the same local cleanup
done in the BLE_GAP_EVENT_DISCONNECT branch, which can leave
m_connHandle/m_connStatus stale and prevent deleteOnDisconnect/deleteClient from
running; fix by performing the same finalization steps (clear m_connHandle, set
m_connStatus to DISCONNECTED or appropriate terminal state, set m_lastErr if
needed, and run any local cleanup/finalization hooks) before returning true in
the BLE_HS_ENOTCONN and BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) cases inside
disconnect(), so the client is not left in a phantom-connected state and
deleteClient()/disconnect() logic can proceed normally.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/NimBLEClient.cpp`:
- Around line 381-393: The fast-path in NimBLEClient::disconnect currently
returns true for BLE_HS_ENOTCONN and BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) without
performing the same local cleanup done in the BLE_GAP_EVENT_DISCONNECT branch,
which can leave m_connHandle/m_connStatus stale and prevent
deleteOnDisconnect/deleteClient from running; fix by performing the same
finalization steps (clear m_connHandle, set m_connStatus to DISCONNECTED or
appropriate terminal state, set m_lastErr if needed, and run any local
cleanup/finalization hooks) before returning true in the BLE_HS_ENOTCONN and
BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) cases inside disconnect(), so the client is
not left in a phantom-connected state and deleteClient()/disconnect() logic can
proceed normally.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 02c6c787-d38f-4cb2-98b5-36199dd51cf4

📥 Commits

Reviewing files that changed from the base of the PR and between 0a0dbaa and 91251ea.

📒 Files selected for processing (2)
  • src/NimBLEClient.cpp
  • src/NimBLEServer.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/NimBLEServer.cpp

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/NimBLEClient.cpp (1)

381-388: ⚠️ Potential issue | 🟠 Major

Arrr, unknown-conn-id success path leaves local state adrift.

On Line 387 through Line 388, returnin’ true for BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) without normalizin’ m_connStatus/m_connHandle can leave the client thinkin’ it still has a live link when the controller says otherwise.

⚓ Proposed patch
 bool NimBLEClient::disconnect(uint8_t reason) {
     int rc = ble_gap_terminate(m_connHandle, reason);
     switch (rc) {
         case 0:
             m_connStatus = DISCONNECTING;
             return true;
         case BLE_HS_ENOTCONN:
+            m_connHandle = BLE_HS_CONN_HANDLE_NONE;
+            m_connStatus = DISCONNECTED;
+            return true;
         case BLE_HS_EALREADY:
             return true;
         case BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID): // should not happen but just in case
+            m_connHandle = BLE_HS_CONN_HANDLE_NONE;
+            m_connStatus = DISCONNECTED;
             return true;
     }

     NIMBLE_LOGE(LOG_TAG, "ble_gap_terminate failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
     m_lastErr = rc;
     return false;
 } // disconnect

Also applies to: 391-393

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NimBLEClient.cpp` around lines 381 - 388, The
BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) path returns true without clearing local
connection state, which can leave m_connStatus/m_connHandle inconsistent; update
the handler(s) for BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) (the cases in the switch
that currently return true) to normalize state by setting m_connStatus to
DISCONNECTED and resetting m_connHandle to the sentinel (e.g.,
BLE_HS_CONN_HANDLE_NONE or your project's invalid-handle value) before
returning; apply the same fix to the other identical case block mentioned (the
duplicate at the later switch).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/NimBLEClient.cpp`:
- Around line 381-388: The BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) path returns true
without clearing local connection state, which can leave
m_connStatus/m_connHandle inconsistent; update the handler(s) for
BLE_HS_HCI_ERR(BLE_ERR_UNK_CONN_ID) (the cases in the switch that currently
return true) to normalize state by setting m_connStatus to DISCONNECTED and
resetting m_connHandle to the sentinel (e.g., BLE_HS_CONN_HANDLE_NONE or your
project's invalid-handle value) before returning; apply the same fix to the
other identical case block mentioned (the duplicate at the later switch).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6724b75e-a38d-4a8f-a536-4037252b737e

📥 Commits

Reviewing files that changed from the base of the PR and between 0a0dbaa and 91251ea.

📒 Files selected for processing (2)
  • src/NimBLEClient.cpp
  • src/NimBLEServer.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/NimBLEServer.cpp

@h2zero h2zero merged commit 182796d into master Mar 28, 2026
66 checks passed
@h2zero h2zero deleted the bugfix/disconnect-unk-conn-id branch March 28, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant