ruvnet/RuView · error
cannot bind {addr}: {e}
Error message
cannot bind {addr}: {e} What it means
enroll failed to UdpSocket::bind its CSI receive address {bind}:{udp_port}. Same failure family as the calibrate-serve ingest bind: AddrInUse when another CSI tool (calibrate-serve, room-watch, a second enroll) holds the UDP port, permission denied on ports below 1024, or an invalid --bind address.
Source
Thrown at v2/crates/wifi-densepose-cli/src/room.rs:149
let (anchor, reason) = recorder.finalize(gate, now_unix());
let feature = if anchor.quality.accepted {
Some(AnchorFeature::from_series(room_id, label, &series, fs_hz))
} else {
None
};
Ok((feature, anchor, reason))
}
/// Execute `enroll`.
pub async fn enroll(args: EnrollArgs) -> Result<()> {
let baseline = load_baseline(&args.baseline)?;
let baseline_id = baseline.calibration_uuid().to_string();
let gate = AnchorQualityGate::default();
let addr = format!("{}:{}", args.bind, args.udp_port);
let socket = UdpSocket::bind(&addr)
.await
.map_err(|e| anyhow::anyhow!("cannot bind {addr}: {e}"))?;
eprintln!("[enroll] room='{}' baseline={} on udp://{addr}", args.room_id, &baseline_id[..8]);
eprintln!("[enroll] follow each prompt; bad captures are re-prompted.");
let mut session = EnrollmentSession::new(&args.room_id, &baseline_id, now_unix());
let mut features: Vec<AnchorFeature> = Vec::new();
for label in AnchorLabel::SEQUENCE {
let mut accepted = false;
for attempt in 1..=args.attempts {
let (feat, anchor, reason) =
capture_anchor(&socket, &baseline, &gate, label, &args.tier, args.fs_hz, &args.room_id)
.await?;
if anchor.quality.accepted {
eprintln!(
"[enroll] ✓ accepted (presence_z={:.2} motion={:.0}% frames={})",
anchor.quality.presence_z,
anchor.quality.motion_rate * 100.0,
anchor.quality.framesView on GitHub (pinned to 4685618388)
Solutions
- Stop the other CSI-consuming process or give enroll its own --udp-port
- Match the firmware's CSI destination port to the one enroll binds
- Use a high port; avoid ports below 1024 unless privileged
- Re-run enroll once the port is free — the baseline file is not affected
Example fix
// before $ ruview calibrate-serve & # still bound to udp/9999 $ ruview enroll --udp-port 9999 # cannot bind // after $ ruview calibrate-serve & $ ruview enroll --udp-port 9998 # dedicated port, firmware retargeted
Defensive patterns
Strategy: validation
Validate before calling
fn udp_port_free(bind: &str, port: u16) -> bool {
std::net::UdpSocket::bind((bind, port)).is_ok()
} Try / catch
match UdpSocket::bind(&addr).await {
Ok(socket) => socket,
Err(e) if e.kind() == std::io::ErrorKind::AddrInUse => {
return Err(anyhow::anyhow!("udp://{addr} already in use — stop calibrate-serve/room-watch or change --udp-port"));
}
Err(e) => return Err(anyhow::anyhow!("cannot bind {addr}: {e}")),
} Prevention
- Run only one CSI ingest tool per port at a time; sequence calibrate, enroll, train-room with explicit handoff
- Document the assigned UDP port per host
- Probe the port before starting interactive enrollment so prompts never dead-air
When it happens
Trigger: Running enroll while calibrate-serve or room-watch is bound to the same port (all CSI ingest tools compete for the UDP port); a privileged port as non-root; a malformed --bind value.
Common situations: Following the calibrate to enroll workflow without stopping the calibration server; parallel sessions on one host; the port changed on the firmware side only.
Related errors
- cannot bind UDP socket on {udp_addr}: {e}
- cannot bind HTTP listener on {http_addr}: {e}
- cannot bind UDP socket on {addr}: {e}
- cannot write {}: {e}
- cannot read {}: {e} — run `enroll` first
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/944a44780a2b7d0a.
Report an issue: GitHub.