GyulyVGC/sniffnet · error · LatencyStatus::Failed
error.to_string()
Error message
error.to_string()
What it means
In measure_latency (src/networking/types/latency.rs:26), before pinging, client_for(ip) lazily builds a surge-ping ICMP Client (Client::new(&config) in latency_client, latency.rs:75). If constructing the client fails, the function immediately returns LatencyStatus::Failed(error.to_string()) and the error text is displayed next to the connection's RTT column. Typical cause: the OS refuses to create the ICMP socket, i.e. missing raw-socket privileges (Linux) or an unsupported/withdrawn IPv6 stack.
Source
Thrown at src/networking/types/latency.rs:26
const PING_TIMEOUT: Duration = Duration::from_secs(2);
const PING_PAYLOAD: [u8; 8] = [0; 8];
const PING_COUNT: usize = 3;
static IPV4_CLIENT: OnceLock<Arc<Client>> = OnceLock::new();
static IPV6_CLIENT: OnceLock<Arc<Client>> = OnceLock::new();
static PING_SEQUENCE: AtomicU16 = AtomicU16::new(0);
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum LatencyStatus {
Measuring,
Measured(Duration),
Failed(String),
}
pub async fn measure_latency(ip: IpAddr) -> LatencyStatus {
let client = match client_for(ip) {
Ok(client) => client,
Err(error) => return LatencyStatus::Failed(error),
};
let mut pinger = client.pinger(ip, ping_identifier()).await;
pinger.timeout(PING_TIMEOUT);
let mut sum = Duration::ZERO;
let mut received: u32 = 0;
let mut last_error = None;
for _ in 0..PING_COUNT {
match pinger.ping(next_sequence(), &PING_PAYLOAD).await {
Ok((_, latency)) => {
sum += latency;
received += 1;
}
Err(error @ SurgeError::Timeout { .. }) => last_error = Some(error.to_string()),
Err(error) => {
last_error = Some(error.to_string());
break;View on GitHub (pinned to 48b0575dc0)
Solutions
- Grant raw-socket capability to the binary: `sudo setcap cap_net_raw+eip $(which sniffnet)` (covers both capture and ICMP), or run with sudo.
- In containers, add the capability: `docker run --cap-add=NET_RAW ...`.
- For IPv6 failures, verify the stack is enabled (`sysctl net.ipv6.conf.all.disable_ipv6` should be 0) or disable IPv6 DNS resolution for the app.
- If privileges can't be changed, disable the latency-measurement feature in settings so the Failed text stops appearing.
- Check the exact text: it is the raw surge-ping/IO error (e.g. 'Operation not permitted (os error 1)'), which distinguishes privilege vs protocol-unavailable.
Example fix
# before — unprivileged binary, ICMP client creation fails $ sniffnet # latency column shows 'Failed: Operation not permitted (os error 1)' # after — grant CAP_NET_RAW to the executable $ sudo setcap cap_net_raw,cap_net_admin+eip $(readlink -f $(which sniffnet)) $ sniffnet # latency values appear # container equivalent $ docker run --cap-add=NET_RAW --rm sniffnet
Defensive patterns
Strategy: try-catch
Validate before calling
// probe ICMP client creation once at startup and disable the feature if unavailable
fn latency_supported(ip_kind: icmp_ns::ICMP) -> bool {
let config = match ip_kind {
icmp_ns::ICMP::V4 => icmp_ns::Config::default(),
icmp_ns::ICMP::V6 => icmp_ns::Config::builder().kind(icmp_ns::ICMP::V6).build(),
};
icmp_ns::Client::new(&config).is_ok()
} Try / catch
// treat Failed as a display state, not an error path
match measure_latency(ip).await {
LatencyStatus::Measured(d) => show_rtt(d),
LatencyStatus::Measuring => show_spinner(),
LatencyStatus::Failed(why) => show_muted(why), // e.g. 'Operation not permitted (os error 1)' Prevention
- Grant CAP_NET_RAW to the binary (`setcap cap_net_raw+eip`) or run elevated so both capture and ICMP work.
- In containers, always pass --cap-add=NET_RAW.
- Verify IPv6 is enabled (`sysctl net.ipv6.conf.all.disable_ipv6`) before expecting IPv6 latencies.
- Disable the latency feature in settings on hosts where ICMP sockets cannot be created.
When it happens
Trigger: Enabling latency measurement and clicking a host whose IP triggers first Client::new: IPv4 client creation fails when the process lacks CAP_NET_RAW/root (raw ICMP socket EPERM); IPv6 client creation fails with Config::builder().kind(ICMP::V6).build() on systems where IPv6 is disabled (socket creation error). Once a client fails, every subsequent measurement for that address family reuses the same failed path until the process restarts — actually it retries creation each time since only successful clients are cached in the OnceLock.
Common situations: Running Sniffnet unprivileged on Linux: capture may work via a setcap'd binary but the ping socket still needs privileges (or vice versa); running inside containers without CAP_NET_RAW; macOS hardened runtime blocking raw sockets; hosts with ipv6.disabled=1 kernel parameter showing Failed for all AAAA/IPv6 hosts; seccomp/container profiles denying socket(AF_INET, SOCK_RAW).
Related errors
- No reply
- e.to_string()
- Sniffnet error at [{file}:{line}]: {e}
- panic!() (bare panic triggered by ErrorLogger on Err in debu
- Could not restore default settings
AI-assisted analysis of GyulyVGC/sniffnet@48b0575dc0 (2026-08-16).
Data as JSON: /api/errors/60533bb517f3dd04.
Report an issue: GitHub.