diff --git a/src/lib.rs b/src/lib.rs index 7a1b951..8260ea2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -163,12 +163,20 @@ impl Drop for Emitter { } } +// A Linux machine without a USB bus (often a container) has no +// /sys/bus/usb/devices. That means there are no devices, not that listing failed. +fn is_missing_usb_bus(e: &nusb::Error) -> bool { + cfg!(target_os = "linux") + && e.os_error().is_some_and(|code| { + std::io::Error::from_raw_os_error(code as i32).kind() == std::io::ErrorKind::NotFound + }) +} + async fn list_devices(error_prefix: &'static str) -> Result> { - run_blocking(move || { - nusb::list_devices() - .wait() - .map(|devices| devices.collect::>()) - .map_err(|e| format!("{error_prefix} error: {e}")) + run_blocking(move || match nusb::list_devices().wait() { + Ok(devices) => Ok(devices.collect::>()), + Err(e) if is_missing_usb_bus(&e) => Ok(Vec::new()), + Err(e) => Err(format!("{error_prefix} error: {e}")), }) .await } diff --git a/tsc/index.ts b/tsc/index.ts index 5bf0db3..e06d804 100644 --- a/tsc/index.ts +++ b/tsc/index.ts @@ -148,7 +148,9 @@ class WebUSB extends EventTarget implements USB { private queueNativeEmitterOperation(operation: () => Promise): void { const pending = this.nativeEmitterQueue.then(operation); this.nativeEmitterQueue = pending.catch(() => {}); - pending.catch(error => setTimeout(() => { throw error; }, 0)); + // A hotplug watcher that can't start (e.g. no netlink in a container) must + // not crash the host process, so report it rather than rethrowing. + pending.catch(error => console.warn('usb: hotplug watcher error:', error)); } private deviceConnectCallback = async (device: UsbDevice) => { @@ -194,7 +196,9 @@ class WebUSB extends EventTarget implements USB { await this.nativeEmitter.addAttach(this.deviceConnectCallback); await this.nativeEmitter.addDetach(this.deviceDisconnectCallback); }); - nativeGetDevices().then(devices => devices.forEach(device => this.knownDevices.set(device.handle, device))); + nativeGetDevices() + .then(devices => devices.forEach(device => this.knownDevices.set(device.handle, device))) + .catch(error => console.warn('usb: failed to list devices for hotplug:', error)); } }