From 0428a50fc0ba8e4c1a39de8800b1b917acdea597 Mon Sep 17 00:00:00 2001 From: Brian Hotopp Date: Tue, 17 Mar 2026 21:56:23 -0400 Subject: [PATCH] Include underlying error details in commissioning failure messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Matter commissioning fails, the ChipStackError (e.g. invalid pairing code, wrong code length, network not found) was being caught and re-raised with a generic message like "Commission with code failed for node X" — discarding the specific error. Users had to enable verbose logging and read tracebacks to diagnose issues. This change appends the original error message to all commissioning- related exceptions (NodeCommissionFailed, NodeInterviewFailed, NodeNotResolving), making failures self-diagnosable from the UI. Also adds a helpful hint to the Bluetooth availability error suggesting users configure a Bluetooth adapter or use network_only mode. Co-Authored-By: Claude Opus 4.6 (1M context) --- matter_server/server/device_controller.py | 14 ++++++++++---- matter_server/server/sdk.py | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/matter_server/server/device_controller.py b/matter_server/server/device_controller.py index 9df276c1..41131004 100644 --- a/matter_server/server/device_controller.py +++ b/matter_server/server/device_controller.py @@ -318,7 +318,11 @@ async def commission_with_code( :return: The NodeInfo of the commissioned device. """ if not network_only and not self.server.bluetooth_enabled: - raise NodeCommissionFailed("Bluetooth commissioning is not available.") + raise NodeCommissionFailed( + "Bluetooth commissioning is not available. " + "Ensure a Bluetooth adapter is configured in the Matter Server settings, " + "or use network_only mode if the device is already on the network." + ) node_id = self._get_next_node_id() LOGGER.info( @@ -342,7 +346,7 @@ async def commission_with_code( raise RuntimeError("Returned Node ID must match requested Node ID") except ChipStackError as err: raise NodeCommissionFailed( - f"Commission with code failed for node {node_id}." + f"Commission with code failed for node {node_id}: {err}" ) from err LOGGER.info("Matter commissioning of Node ID %s successful.", node_id) @@ -428,7 +432,7 @@ async def commission_on_network( raise RuntimeError("Returned Node ID must match requested Node ID") except ChipStackError as err: raise NodeCommissionFailed( - f"Commissioning failed for node {node_id}." + f"Commissioning failed for node {node_id}: {err}" ) from err LOGGER.info("Matter commissioning of Node ID %s successful.", node_id) @@ -595,7 +599,9 @@ async def _interview_node(self, node_id: int) -> None: ) ) except ChipStackError as err: - raise NodeInterviewFailed(f"Failed to interview node {node_id}") from err + raise NodeInterviewFailed( + f"Failed to interview node {node_id}: {err}" + ) from err # Set label if specified and needed if self._default_fabric_label: diff --git a/matter_server/server/sdk.py b/matter_server/server/sdk.py index 3add3f83..204f73a9 100644 --- a/matter_server/server/sdk.py +++ b/matter_server/server/sdk.py @@ -398,7 +398,7 @@ async def find_or_establish_case_session( if attempt >= retries: # when we're out of retries, raise NodeNotResolving raise NodeNotResolving( - f"Unable to establish CASE session with Node {node_id}" + f"Unable to establish CASE session with Node {node_id}: {err}" ) from err await asyncio.sleep(2 + attempt) finally: