{"record":{"id":"12049dd505ac04c7","repo":"tonhowtf/omniget","slug":"relay-closed-connection-unexpectedly","errorCode":null,"errorMessage":"Relay closed connection unexpectedly","messagePattern":"Relay closed connection unexpectedly","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/p2p.rs","lineNumber":40,"sourceCode":"async fn connect_relay() -> anyhow::Result<TcpStream> {\n    let addr = relay_addr();\n    let stream = tokio::time::timeout(\n        std::time::Duration::from_secs(10),\n        TcpStream::connect(&addr),\n    )\n    .await\n    .map_err(|_| anyhow!(\"Connection to relay timed out (10s)\"))?\n    .map_err(|e| anyhow!(\"Failed to connect to relay {}: {}\", addr, e))?;\n    Ok(stream)\n}\n\nasync fn read_line(\n    reader: &mut BufReader<tokio::io::ReadHalf<TcpStream>>,\n) -> anyhow::Result<String> {\n    let mut line = String::new();\n    let n = reader.read_line(&mut line).await?;\n    if n == 0 {\n        anyhow::bail!(\"Relay closed connection unexpectedly\");\n    }\n    Ok(line.trim_end().to_string())\n}\n\nfn check_relay_error(line: &str) -> anyhow::Result<()> {\n    if let Some(err) = line.strip_prefix(\"ERROR \") {\n        anyhow::bail!(\"Relay error: {}\", err);\n    }\n    Ok(())\n}\n\npub struct P2pDownloader;\n\nimpl P2pDownloader {\n    pub fn new() -> Self {\n        Self\n    }\n}","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/p2p.rs#L22-L58","documentation":"p2p.rs throws this in read_line() when the TCP read from the relay returns 0 bytes, i.e. the relay closed the connection (EOF) while a line was still expected. It guards against a peer/relay disappearing mid-protocol, converting tokio's silent EOF into an explicit error.","triggerScenarios":"Any call to read_line() where reader.read_line(&mut line).await? returns n == 0 — relay process stopped, network dropped, relay timed out the connection, or the sender/receiver closed the socket early. Called from both download and run_sender.","commonSituations":"Relay server restarted or crashed mid-transfer; NAT/firewall idle timeout dropped the TCP connection; remote peer quit the transfer; unstable network (Wi-Fi/mobile) between client and relay.","solutions":["Retry the connection to the relay with backoff if the transfer is resumable.","Check that the relay service is running and reachable (relay address/port correct, process alive).","Add TCP keepalive / application-level heartbeat so idle hops are not silently dropped by NAT.","Log the transfer stage at EOF to see which protocol step lost the connection and handle partial-transfer cleanup.","If the relay closes immediately, check relay-side logs for authentication or protocol-version rejections."],"exampleFix":"// before\nlet line = read_line(&mut reader).await?;\n// after\nlet line = match read_line(&mut reader).await {\n    Ok(l) => l,\n    Err(e) if e.to_string() == \"Relay closed connection unexpectedly\" => {\n        warn!(\"relay dropped connection; reconnecting (attempt {})...\", n);\n        reconnect_and_resume().await?;\n        continue;\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"retry","validationCode":"// probe the relay before the transfer\nlet mut probe = TcpStream::connect(relay_addr).await?; // fail fast if relay is down","typeGuard":null,"tryCatchPattern":"match read_line(&mut reader).await {\n    Err(e) if e.to_string() == \"Relay closed connection unexpectedly\" => reconnect_with_backoff().await,\n    other => other,\n}","preventionTips":["Enable TCP keepalive on relay connections","Add application-level heartbeats to survive NAT idle timeouts","Monitor relay process health and restart it automatically","Persist transfer progress so reconnects can resume"],"tags":["network","p2p","tcp","relay","eof"],"backgroundTag":"broken-pipe","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}