{"record":{"id":"7ed7b26afeb300a9","repo":"astrid-runtime/astrid","slug":"fuse-service-control-request-exceeds-limit","errorCode":null,"errorMessage":"FUSE service control request exceeds limit","messagePattern":"FUSE service control request exceeds limit","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-fuse/src/service.rs","lineNumber":123,"sourceCode":"async fn service_loop(\n    listener: &tokio::net::UnixListener,\n    launch: &StorageProviderServiceLaunchV1,\n    session: &mut Option<FuseBackgroundSession>,\n) -> Result<()> {\n    let mut poll = tokio::time::interval(SERVICE_POLL);\n    loop {\n        tokio::select! {\n            accepted = listener.accept() => {\n                let (mut stream, _) = accepted.context(\"accept FUSE service control\")?;\n                let mut line = String::new();\n                let reader = tokio::io::BufReader::new(&mut stream);\n                let read = reader\n                    .take((MAX_CONTROL_BYTES + 1) as u64)\n                    .read_line(&mut line)\n                    .await\n                    .context(\"read FUSE service control request\")?;\n                if read == 0 || line.len() > MAX_CONTROL_BYTES {\n                    bail!(\"FUSE service control request exceeds limit\");\n                }\n                let request: KernelControlRequest = serde_json::from_str(&line)\n                    .context(\"decode FUSE service control request\")?;\n                let (response, stop) = match request {\n                    KernelControlRequest::Status { token } if token == launch.parent.token => {\n                        (KernelControlResponse::Ready, false)\n                    },\n                    KernelControlRequest::Stop { token } if token == launch.parent.token => {\n                        let response = match session.take() {\n                            Some(session) => match tokio::task::spawn_blocking(|| session.umount_and_join()).await {\n                                Ok(Ok(())) => KernelControlResponse::Stopped,\n                                Ok(Err(error)) => failure_response(\"unmount\", &error.to_string()),\n                                Err(error) => failure_response(\"unmount\", &error.to_string()),\n                            },\n                            None => KernelControlResponse::Stopped,\n                        };\n                        if matches!(&response, KernelControlResponse::Stopped) {\n                            let _ = local_transport::remove_endpoint(&launch.control_path);","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-fuse/src/service.rs#L105-L141","documentation":"The FUSE control socket reads newline-delimited JSON requests capped at MAX_CONTROL_BYTES. A line that is empty (EOF) or longer than the limit is rejected with this error instead of being parsed, protecting the service from oversized or truncated control requests.","triggerScenarios":"`service_loop` reads from the control connection with `take(MAX_CONTROL_BYTES + 1).read_line()`, and either the peer closed the connection (`read == 0`) or the received line exceeds MAX_CONTROL_BYTES, causing `bail!`.","commonSituations":"A client sends a huge or malformed request to the control socket; a truncated write leaves a line without a newline so read_line hits the cap; a client connects and disconnects without sending anything; a probe/writer bug double-encodes payloads.","solutions":["Keep control requests under MAX_CONTROL_BYTES — trim payloads (e.g. paths, tokens) sent to the socket.","Ensure every request is a single newline-terminated JSON line written atomically.","Check the client for errors that cause premature disconnect (read==0) or partial writes.","Serialize requests with serde_json to one compact line rather than hand-rolled formatting.","If legitimate requests need more space, raise MAX_CONTROL_BYTES in the service (both sides) consistently."],"exampleFix":"// before\nstream.write_all(payload.as_bytes())?; // missing newline / oversized\n// after\nlet line = serde_json::to_string(&request)?;\nassert!(line.len() <= MAX_CONTROL_BYTES);\nstream.write_all(line.as_bytes())?;\nstream.write_all(b\"\\n\")?;","handlingStrategy":"validation","validationCode":"let line = serde_json::to_string(&request)?;\nif line.len() + 1 > MAX_CONTROL_BYTES {\n    return Err(\"control request too large\");\n}\nassert!(line.contains('\\n') == false);","typeGuard":null,"tryCatchPattern":"match write_request(stream, &req).await {\n    Err(e) if e.to_string().contains(\"exceeds limit\") => trim_payload_and_retry(),\n    other => other,\n}","preventionTips":["Write each request as one newline-terminated compact JSON line","Keep payloads well under MAX_CONTROL_BYTES","Handle server EOF: reconnect before sending again","Keep MAX_CONTROL_BYTES in sync between client and service"],"tags":["fuse","ipc","protocol","payload-too-large"],"backgroundTag":"payload-too-large","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}