{"record":{"id":"2e33b9459187a7bc","repo":"spacedriveapp/spacedrive","slug":"failed-to-read-response","errorCode":null,"errorMessage":"Failed to read response: {}","messagePattern":"Failed to read response: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"core/src/service/network/protocol/sync/transport.rs","lineNumber":193,"sourceCode":"\t\tsend.finish()\n\t\t\t.map_err(|e| anyhow::anyhow!(\"Failed to finish stream: {}\", e))?;\n\n\t\tdebug!(\"Sync request sent, waiting for response...\");\n\n\t\t// Read response with timeout\n\t\tlet result = timeout(Duration::from_secs(60), async {\n\t\t\tlet mut len_buf = [0u8; 4];\n\t\t\trecv.read_exact(&mut len_buf).await.map_err(|e| {\n\t\t\t\tanyhow::anyhow!(\"Failed to read response length: {}\", e)\n\t\t\t})?;\n\t\t\tlet resp_len = u32::from_be_bytes(len_buf) as usize;\n\n\t\t\tdebug!(\"Receiving sync response of {} bytes\", resp_len);\n\n\t\t\tlet mut resp_buf = vec![0u8; resp_len];\n\t\t\trecv.read_exact(&mut resp_buf)\n\t\t\t\t.await\n\t\t\t\t.map_err(|e| anyhow::anyhow!(\"Failed to read response: {}\", e))?;\n\t\t\tOk::<_, anyhow::Error>(resp_buf)\n\t\t})\n\t\t.await;\n\n\t\tlet resp_buf = match result {\n\t\t\tOk(Ok(buf)) => buf,\n\t\t\tOk(Err(e)) => return Err(e),\n\t\t\tErr(_) => {\n\t\t\t\treturn Err(anyhow::anyhow!(\n\t\t\t\t\t\"Sync request timed out after 60s - peer {} not responding\",\n\t\t\t\t\ttarget_device\n\t\t\t\t))\n\t\t\t}\n\t\t};\n\n\t\t// Deserialize response\n\t\tlet response: SyncMessage = serde_json::from_slice(&resp_buf)\n\t\t\t.map_err(|e| anyhow::anyhow!(\"Failed to deserialize sync response: {}\", e))?;","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/spacedriveapp/spacedrive/blob/6dfeccf2113039e35f2ce735f945e70dc3e4ea45/core/src/service/network/protocol/sync/transport.rs#L175-L211","documentation":"After reading the 4-byte length, reading the announced response body failed. The stream ended before resp_len bytes arrived (truncated response) or the connection dropped mid-body - the peer started a response it never finished.","triggerScenarios":"Peer killed while serializing or sending a large response; peer wrote fewer bytes than its length prefix claimed (framing bug); network cutoff mid-transfer.","commonSituations":"First sync of a large library producing a big diff response; peer OOM during response construction; flaky link.","solutions":["Retry the request - truncation is usually transient","If reproducible, log resp_len and peer-side sent-byte counts to find the framing bug","Stream large responses in chunks instead of one length-prefixed blob","Sanity-check resp_len before allocating the buffer"],"exampleFix":"// before\nlet mut resp_buf = vec![0u8; resp_len];\n\n// after\nconst MAX_RESPONSE_BYTES: usize = 256 * 1024 * 1024;\nif resp_len > MAX_RESPONSE_BYTES {\n    anyhow::bail!(\"implausible response length {}\", resp_len);\n}\nlet mut resp_buf = vec![0u8; resp_len];","handlingStrategy":"retry","validationCode":"// Before trusting the length prefix (guards huge allocations from corrupt data)\nconst MAX_RESPONSE_BYTES: usize = 256 * 1024 * 1024;\nif resp_len > MAX_RESPONSE_BYTES {\n    return Err(anyhow::anyhow!(\"implausible response length {}\", resp_len));\n}","typeGuard":null,"tryCatchPattern":"Err(e) if e.to_string().contains(\"Failed to read response\") => {\n    // truncated response: retry once; if reproducible, compare resp_len with peer-side byte counts\n    retry_request_on_fresh_connection(device, request).await\n}","preventionTips":["Validate resp_len before allocating the receive buffer","Retry once; mid-body truncation is usually transient","For large libraries, prefer chunked streaming responses over single blobs"],"tags":["network","iroh","quic","protocol","sync","rust"],"backgroundTag":null,"analyzedSha":"6dfeccf2113039e35f2ce735f945e70dc3e4ea45","analyzedAt":"2026-08-16T11:26:17.074Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}