{"record":{"id":"3658d617fb8490ea","repo":"herdrdev/herdr","slug":"api-request-line-is-too-large","errorCode":null,"errorMessage":"api request line is too large","messagePattern":"api request line is too large","errorType":"error_code","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"src/api/server.rs","lineNumber":535,"sourceCode":"    let mut bytes = Vec::new();\n    let mut byte = [0u8; 1];\n\n    let result = loop {\n        let read = match poll_local_stream_read(stream, &mut byte) {\n            Ok(read) => read,\n            Err(err) => break Err(err),\n        };\n        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","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/herdrdev/herdr/blob/f457cff4f2648eee85d176f8a41861241d4e8428/src/api/server.rs#L517-L553","documentation":"Herdr's local API server reads each client's first line (the request line) byte-by-byte with a hard size cap. If the accumulated bytes exceed max_bytes before a newline arrives, the connection is rejected with ErrorKind::InvalidData and the message 'api request line is too large'. This protects the single-threaded local socket reader from unbounded buffering. Well-formed clients sending a JSON request line under the limit never see it.","triggerScenarios":"Calling the local API socket with a single request line longer than the server's max_bytes limit (e.g. a very large JSON payload inlined into one line), or a client that never sends a newline and keeps streaming bytes past the cap. Produced by read_initial_request_line_with_limits during connection setup.","commonSituations":"Scripts that paste huge base64 blobs or full terminal snapshots into a one-line request; a client speaking a different framing (e.g. sending a binary or multi-line payload where the server expects a length-bounded line); misbehaving or malicious processes connecting to the exposed local socket.","solutions":["Shrink the request payload: send large data (graphics frames, scrollback) through the dedicated streaming endpoints instead of the initial request line.","If you control the client, split the payload across multiple framed requests rather than one giant line.","Check what you are actually writing to the socket; a missing newline plus continued output means the server reads your whole stream as one line.","If a legitimate use case needs a bigger line, raise the max_bytes limit passed to read_initial_request_line_with_limits and rebuild."],"exampleFix":"// before\nlet line = format!(\"{{\\\"type\\\":\\\"write\\\",\\\"data\\\":\\\"{huge_base64}\\\"}}\");\nstream.write_all(line.as_bytes())?;\n\n// after\n// send a small request; stream large payloads via the pane graphics stream endpoint\nstream.write_all(b\"{\\\"type\\\":\\\"write\\\",\\\"bytes\\\":8192}\\n\")?;","handlingStrategy":"validation","validationCode":"const MAX_REQUEST_LINE: usize = 64 * 1024; // must match server limit\nlet line = format!(\"{req}\\n\");\nassert!(line.len() <= MAX_REQUEST_LINE, \"request line {} bytes exceeds limit\", line.len());","typeGuard":null,"tryCatchPattern":"match read_initial_request_line_with_timeout(&mut stream, timeout) {\n    Err(e) if e.kind() == io::ErrorKind::InvalidData => {\n        // line exceeded cap: shrink payload or use streaming endpoint\n    }\n    other => other,\n}","preventionTips":["Keep the initial request line small; move large payloads to streaming endpoints.","Always terminate the request line with a single newline.","Add a client-side length assert before writing to the socket."],"tags":["api","request-size","local-socket","rust"],"backgroundTag":"request-entity-too-large","analyzedSha":"f457cff4f2648eee85d176f8a41861241d4e8428","analyzedAt":"2026-08-28T15:41:09.197Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}