{"record":{"id":"8a4838dfb23e69d4","repo":"embassy-rs/embassy","slug":"read-error","errorCode":null,"errorMessage":"read error: {:?}","messagePattern":"read error: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-net-tuntap/src/lib.rs","lineNumber":184,"sourceCode":"    }\n\n    fn register_waker(&mut self, waker: &Waker) -> Result<(), NotSupported> {\n        let mut cx = Context::from_waker(waker);\n        let _ = self.device.poll_readable(&mut cx);\n        Ok(())\n    }\n\n    fn receive(&mut self) -> Option<PacketBuf> {\n        let mut buf = PacketBuf::try_new()?;\n        let mtu = self.device.get_ref().mtu.min(buf.capacity());\n        buf.set_len(mtu);\n        match unsafe { self.device.get_mut() }.read(&mut buf) {\n            Ok(n) => {\n                buf.set_len(n);\n                Some(buf)\n            }\n            Err(e) if e.kind() == io::ErrorKind::WouldBlock => None,\n            Err(e) => panic!(\"read error: {:?}\", e),\n        }\n    }\n\n    fn can_transmit(&mut self) -> bool {\n        true\n    }\n\n    fn transmit(&mut self, buf: PacketBuf) -> Result<(), PacketBuf> {\n        // todo handle WouldBlock with async\n        match unsafe { self.device.get_mut() }.write(&buf) {\n            Ok(_) => {}\n            Err(e) if e.kind() == io::ErrorKind::WouldBlock => info!(\"transmit WouldBlock\"),\n            Err(e) => panic!(\"transmit error: {:?}\", e),\n        }\n        Ok(())\n    }\n}\n","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-net-tuntap/src/lib.rs#L166-L202","documentation":"The tuntap device panicked because reading a packet from the host's TUN device file failed with an error other than WouldBlock (WouldBlock is treated as 'no packet yet' and returns None). Since the smoltcp Device trait's receive() cannot return an error, any real I/O error (device closed, interface gone down, permission revoked) becomes a hard panic in the driver task.","triggerScenarios":"The TUN file descriptor read fails with a non-WouldBlock error during receive(): the /dev/net/tun fd was closed, the tun interface was deleted (ip link del), the process lost permission to the device, or an unexpected errno (e.g., EBADF, ENODEV) occurred.","commonSituations":"Running the app without CAP_NET_ADMIN or outside the expected user/group so the TUN device misbehaves; a network manager or cleanup script deleting the tun interface at runtime; container restrictions blocking /dev/net/tun reads; app shutdown ordering closing the fd while the network task is still polling.","solutions":["Check kernel logs / errno: run with strace or add logging to identify the exact io::ErrorKind before it panics","Ensure the tun interface exists for the process lifetime (do not let scripts or NetworkManager delete it); create it with 'ip tuntap add ... mode tun' owned by the running user","Verify /dev/net/tun is available and permitted (inside containers: --device /dev/net/tun --cap-add NET_ADMIN)","Shut down the network task before closing the tun fd, or keep the fd open for the process lifetime","Patch the driver locally to log-and-continue or return None on non-fatal errors instead of panicking, if graceful degradation is required"],"exampleFix":"// before\nErr(e) => panic!(\"read error: {:?}\", e),\n// after (local patch)\nErr(e) => {\n    warn!(\"tun read error: {:?}\", e);\n    None\n}","handlingStrategy":"try-catch","validationCode":"// preflight: ensure tun interface and /dev/net/tun are usable before starting\n// ip tuntap add mode tun dev tun0 user $USER && ip link set tun0 up\n// test -c /dev/net/tun || { echo 'no /dev/net/tun'; exit 1; }","typeGuard":"// Rust: distinguish benign vs fatal errors before they reach the driver\nfn is_benign(e: &std::io::Error) -> bool {\n    matches!(e.kind(), std::io::ErrorKind::WouldBlock | std::io::ErrorKind::Interrupted)\n}","tryCatchPattern":"// run the network task under catch_unwind and restart cleanly\nlet result = std::panic::catch_unwind(AssertUnwindSafe(|| network_task()));\nif result.is_err() {\n    error!(\"network task panicked; re-creating tun device and restarting\");\n}","preventionTips":["Create the tun interface with an owning user so runtime permission issues don't occur","Prevent NetworkManager/scripts from deleting the interface while the app runs","In containers, pass --device /dev/net/tun --cap-add NET_ADMIN","Keep the tun fd open until the network task fully shuts down","Run under catch_unwind with restart logic for resilience"],"tags":["linux","tun-tap","network","io-error","panic"],"backgroundTag":"file-read-failed","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}