{"record":{"id":"d796f80875d09b4f","repo":"herdrdev/herdr","slug":"timed-out-reading-api-request","errorCode":null,"errorMessage":"timed out reading api request","messagePattern":"timed out reading api request","errorType":"error_code","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"src/api/server.rs","lineNumber":543,"sourceCode":"        match read {\n            LocalStreamRead::Closed => break Ok(None),\n            LocalStreamRead::Data => {\n                bytes.push(byte[0]);\n                if byte[0] == b'\\n' {\n                    break String::from_utf8(bytes)\n                        .map(Some)\n                        .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err));\n                }\n                if bytes.len() > max_bytes {\n                    break Err(io::Error::new(\n                        io::ErrorKind::InvalidData,\n                        \"api request line is too large\",\n                    ));\n                }\n            }\n            LocalStreamRead::Pending => {\n                if Instant::now() >= deadline {\n                    break Err(io::Error::new(\n                        io::ErrorKind::TimedOut,\n                        \"timed out reading api request\",\n                    ));\n                }\n                std::thread::sleep(CONNECTION_POLL_INTERVAL);\n            }\n        }\n    };\n    set_local_stream_polling(stream, false)?;\n    result\n}\n\n#[cfg(all(test, windows))]\nmod windows_tests {\n    use super::*;\n    use interprocess::local_socket::traits::Listener as _;\n    use std::io::{BufRead, BufReader};\n    use std::sync::mpsc::{self, Receiver};","sourceCodeStart":525,"sourceCodeEnd":561,"githubUrl":"https://github.com/herdrdev/herdr/blob/f457cff4f2648eee85d176f8a41861241d4e8428/src/api/server.rs#L525-L561","documentation":"While reading a client's initial request line, the server polls the local stream in small intervals (CONNECTION_POLL_INTERVAL). If the stream stays Pending (no data) until the deadline, the read aborts with ErrorKind::TimedOut and 'timed out reading api request'. This bounds how long a connected-but-silent client can hold the reader. It fires only before any complete line has arrived.","triggerScenarios":"A client opens the local API socket but sends no bytes (or no newline-terminated line) before the read deadline in read_initial_request_line_with_limits; e.g. connect() without a write, a paused client, or a probe tool that just opens the socket.","commonSituations":"Health-check scripts that only open a connection; a client blocked on its own I/O before sending; network/VM file-descriptor stalls on the local socket; leftover sockets from a crashed client.","solutions":["Make the client send its complete newline-terminated request line immediately after connecting.","If the client legitimately waits (e.g. user input), send the request only after it is ready rather than connecting early and idling.","Increase the timeout passed to read_initial_request_line_with_timeout if slow clients are expected.","Verify the client and server agree on the line framing (trailing newline required)."],"exampleFix":"// before\nlet mut stream = UnixStream::connect(path)?;\nthread::sleep(Duration::from_secs(30));\nstream.write_all(req.as_bytes())?;\n\n// after\nlet mut stream = UnixStream::connect(path)?;\nstream.write_all(req.as_bytes())?;\nstream.write_all(b\"\\n\")?;","handlingStrategy":"retry","validationCode":"let mut stream = UnixStream::connect(path)?;\nstream.set_write_timeout(Some(Duration::from_secs(5)))?;\n// write the full line immediately after connect, before anything else\nstream.write_all(request_line_with_newline.as_bytes())?;","typeGuard":null,"tryCatchPattern":"match read_initial_request_line_with_timeout(&mut stream, timeout) {\n    Err(e) if e.kind() == io::ErrorKind::TimedOut => {\n        // client never sent data: reconnect and resend once\n    }\n    other => other,\n}","preventionTips":["Send the complete request immediately after connecting; never connect and idle.","Confirm the client appends the trailing newline the server needs.","Close sockets promptly on client shutdown so no half-open connections linger."],"tags":["api","timeout","local-socket","rust"],"backgroundTag":"read-timeout","analyzedSha":"f457cff4f2648eee85d176f8a41861241d4e8428","analyzedAt":"2026-08-28T15:41:09.197Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}