Error Handling

nmrs uses a single error type, ConnectionError, for all operations. Each variant describes a specific failure mode, making it straightforward to handle errors precisely.

The Result Type

nmrs re-exports a Result type alias:

#![allow(unused)]
fn main() {
pub type Result<T> = std::result::Result<T, ConnectionError>;
}

All public API methods return nmrs::Result<T>.

ConnectionError Variants

Network & Wi-Fi Errors

VariantDescription
NotFoundNetwork not visible during scan
AuthFailedWrong password or rejected credentials
MissingPasswordEmpty password provided
NoWifiDeviceNo Wi-Fi adapter found
WifiNotReadyWi-Fi device not ready in time
NoWiredDeviceNo Ethernet adapter found
DhcpFailedFailed to obtain an IP address via DHCP
TimeoutOperation timed out waiting for activation
Stuck(String)Connection stuck in an unexpected state

Authentication Errors

VariantDescription
SupplicantConfigFailedwpa_supplicant configuration error
SupplicantTimeoutwpa_supplicant timed out during auth

VPN Errors

VariantDescription
NoVpnConnectionVPN not found or not active
VpnFailed(String)VPN connection failed with details
InvalidPrivateKey(String)Bad WireGuard private key
InvalidPublicKey(String)Bad WireGuard public key
InvalidAddress(String)Bad IP address or CIDR notation
InvalidGateway(String)Bad gateway format (host:port)
InvalidPeers(String)Invalid peer configuration

Bluetooth Errors

VariantDescription
NoBluetoothDeviceNo Bluetooth adapter found

Profile Errors

VariantDescription
NoSavedConnectionNo saved profile for the requested network

Low-Level Errors

VariantDescription
Dbus(zbus::Error)D-Bus communication error
DbusOperation { context, source }D-Bus error with context
DeviceFailed(StateReason)Device failure with NM reason code
ActivationFailed(ConnectionStateReason)Activation failure with reason
InvalidUtf8(Utf8Error)Invalid UTF-8 in SSID

Basic Error Handling

Use the ? operator for simple propagation:

use nmrs::{NetworkManager, WifiSecurity};

#[tokio::main]
async fn main() -> nmrs::Result<()> {
    let nm = NetworkManager::new().await?;
    nm.connect("MyWiFi", WifiSecurity::Open).await?;
    Ok(())
}

Pattern Matching

Handle specific errors differently:

#![allow(unused)]
fn main() {
use nmrs::{NetworkManager, WifiSecurity, ConnectionError};

let nm = NetworkManager::new().await?;

match nm.connect("MyWiFi", WifiSecurity::WpaPsk {
    psk: "password".into(),
}).await {
    Ok(_) => println!("Connected!"),
    Err(ConnectionError::NotFound) => {
        eprintln!("Network not in range");
    }
    Err(ConnectionError::AuthFailed) => {
        eprintln!("Wrong password");
    }
    Err(ConnectionError::Timeout) => {
        eprintln!("Connection timed out");
    }
    Err(ConnectionError::DhcpFailed) => {
        eprintln!("Connected to AP but DHCP failed");
    }
    Err(ConnectionError::NoWifiDevice) => {
        eprintln!("No Wi-Fi adapter found");
    }
    Err(e) => eprintln!("Unexpected error: {}", e),
}
}

Retry Logic

Implement retries for transient failures:

#![allow(unused)]
fn main() {
use nmrs::{NetworkManager, WifiSecurity, ConnectionError};

let nm = NetworkManager::new().await?;

for attempt in 1..=3 {
    match nm.connect("MyWiFi", WifiSecurity::WpaPsk {
        psk: "password".into(),
    }).await {
        Ok(_) => {
            println!("Connected on attempt {}", attempt);
            break;
        }
        Err(ConnectionError::Timeout) if attempt < 3 => {
            eprintln!("Attempt {} timed out, retrying...", attempt);
            continue;
        }
        Err(e) => return Err(e),
    }
}
}

VPN Error Handling

#![allow(unused)]
fn main() {
use nmrs::{NetworkManager, ConnectionError};

let nm = NetworkManager::new().await?;

match nm.get_vpn_info("MyVPN").await {
    Ok(info) => println!("VPN IP: {:?}", info.ip4_address),
    Err(ConnectionError::NoVpnConnection) => {
        eprintln!("VPN is not active");
    }
    Err(e) => eprintln!("Error: {}", e),
}
}

Converting to Other Error Types

ConnectionError implements std::error::Error and Display, so it works with error handling crates like anyhow:

#![allow(unused)]
fn main() {
use anyhow::Result;
use nmrs::NetworkManager;

async fn connect() -> Result<()> {
    let nm = NetworkManager::new().await?;
    nm.connect("MyWiFi", nmrs::WifiSecurity::Open).await?;
    Ok(())
}
}

Non-Exhaustive

ConnectionError is marked #[non_exhaustive], which means new variants may be added in future versions without a breaking change. Always include a wildcard arm in match expressions:

#![allow(unused)]
fn main() {
match result {
    Err(ConnectionError::AuthFailed) => { /* ... */ }
    Err(ConnectionError::NotFound) => { /* ... */ }
    Err(e) => { /* catch-all for current and future variants */ }
    Ok(_) => {}
}
}

Next Steps