From c40029d6c7c493e6e822cd6fda86dbcbc82ce8b0 Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Sun, 26 Apr 2026 20:41:38 -0400 Subject: [PATCH] fix(#270): resolve device state reason on activation failure --- nmrs/src/core/state_wait.rs | 38 +++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/nmrs/src/core/state_wait.rs b/nmrs/src/core/state_wait.rs index 36ce0fcf..10574531 100644 --- a/nmrs/src/core/state_wait.rs +++ b/nmrs/src/core/state_wait.rs @@ -27,7 +27,8 @@ use zbus::Connection; use crate::Result; use crate::api::models::{ - ActiveConnectionState, ConnectionError, ConnectionStateReason, connection_state_reason_to_error, + ActiveConnectionState, ConnectionError, ConnectionStateReason, + connection_state_reason_to_error, reason_to_error, }; use crate::dbus::{NMActiveConnectionProxy, NMDeviceProxy}; use crate::types::constants::{device_state, timeouts}; @@ -35,6 +36,29 @@ use crate::types::constants::{device_state, timeouts}; /// Default timeout for connection activation (30 seconds). const CONNECTION_TIMEOUT: Duration = Duration::from_secs(30); +/// When the active connection reports `DeviceDisconnected`, the real failure +/// reason lives on the device itself. Query it and return a more specific error. +async fn refine_device_disconnected_error( + conn: &Connection, + active_conn: &NMActiveConnectionProxy<'_>, +) -> ConnectionError { + if let Ok(devices) = active_conn.devices().await { + for dev_path in &devices { + let Ok(builder) = NMDeviceProxy::builder(conn).path(dev_path.clone()) else { + continue; + }; + let Ok(dev) = builder.build().await else { + continue; + }; + if let Ok((_state, reason_code)) = dev.state_reason().await { + debug!("Device state reason: {reason_code}"); + return reason_to_error(reason_code); + } + } + } + ConnectionError::ActivationFailed(ConnectionStateReason::DeviceDisconnected) +} + /// Default timeout for device disconnection (10 seconds). const DISCONNECT_TIMEOUT: Duration = Duration::from_secs(10); @@ -75,9 +99,7 @@ pub(crate) async fn wait_for_connection_activation( } ActiveConnectionState::Deactivated => { warn!("Connection already deactivated"); - return Err(ConnectionError::ActivationFailed( - ConnectionStateReason::Unknown, - )); + return Err(refine_device_disconnected_error(conn, &active_conn).await); } _ => {} } @@ -97,11 +119,8 @@ pub(crate) async fn wait_for_connection_activation( return Ok(()); } ActiveConnectionState::Deactivated => { - // Connection deactivated between subscription and loop - reason unknown warn!("Connection deactivated during loop"); - return Err(ConnectionError::ActivationFailed( - ConnectionStateReason::Unknown, - )); + return Err(refine_device_disconnected_error(conn, &active_conn).await); } _ => {} } @@ -127,6 +146,9 @@ pub(crate) async fn wait_for_connection_activation( } ActiveConnectionState::Deactivated => { debug!("Connection activation failed: {reason}"); + if reason == ConnectionStateReason::DeviceDisconnected { + return Err(refine_device_disconnected_error(conn, &active_conn).await); + } return Err(connection_state_reason_to_error(args.reason)); } _ => {}