astrid-runtime/astrid · error

FUSE service control request exceeds limit

Error message

FUSE service control request exceeds limit

What it means

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.

Source

Thrown at crates/astrid-storage-provider-fuse/src/service.rs:123

async fn service_loop(
    listener: &tokio::net::UnixListener,
    launch: &StorageProviderServiceLaunchV1,
    session: &mut Option<FuseBackgroundSession>,
) -> Result<()> {
    let mut poll = tokio::time::interval(SERVICE_POLL);
    loop {
        tokio::select! {
            accepted = listener.accept() => {
                let (mut stream, _) = accepted.context("accept FUSE service control")?;
                let mut line = String::new();
                let reader = tokio::io::BufReader::new(&mut stream);
                let read = reader
                    .take((MAX_CONTROL_BYTES + 1) as u64)
                    .read_line(&mut line)
                    .await
                    .context("read FUSE service control request")?;
                if read == 0 || line.len() > MAX_CONTROL_BYTES {
                    bail!("FUSE service control request exceeds limit");
                }
                let request: KernelControlRequest = serde_json::from_str(&line)
                    .context("decode FUSE service control request")?;
                let (response, stop) = match request {
                    KernelControlRequest::Status { token } if token == launch.parent.token => {
                        (KernelControlResponse::Ready, false)
                    },
                    KernelControlRequest::Stop { token } if token == launch.parent.token => {
                        let response = match session.take() {
                            Some(session) => match tokio::task::spawn_blocking(|| session.umount_and_join()).await {
                                Ok(Ok(())) => KernelControlResponse::Stopped,
                                Ok(Err(error)) => failure_response("unmount", &error.to_string()),
                                Err(error) => failure_response("unmount", &error.to_string()),
                            },
                            None => KernelControlResponse::Stopped,
                        };
                        if matches!(&response, KernelControlResponse::Stopped) {
                            let _ = local_transport::remove_endpoint(&launch.control_path);

View on GitHub (pinned to affd8760f4)

Solutions

  1. Keep control requests under MAX_CONTROL_BYTES — trim payloads (e.g. paths, tokens) sent to the socket.
  2. Ensure every request is a single newline-terminated JSON line written atomically.
  3. Check the client for errors that cause premature disconnect (read==0) or partial writes.
  4. Serialize requests with serde_json to one compact line rather than hand-rolled formatting.
  5. If legitimate requests need more space, raise MAX_CONTROL_BYTES in the service (both sides) consistently.

Example fix

// before
stream.write_all(payload.as_bytes())?; // missing newline / oversized
// after
let line = serde_json::to_string(&request)?;
assert!(line.len() <= MAX_CONTROL_BYTES);
stream.write_all(line.as_bytes())?;
stream.write_all(b"\n")?;
Defensive patterns

Strategy: validation

Validate before calling

let line = serde_json::to_string(&request)?;
if line.len() + 1 > MAX_CONTROL_BYTES {
    return Err("control request too large");
}
assert!(line.contains('\n') == false);

Try / catch

match write_request(stream, &req).await {
    Err(e) if e.to_string().contains("exceeds limit") => trim_payload_and_retry(),
    other => other,
}

Prevention

When it happens

Trigger: `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!`.

Common situations: 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.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/7ed7b26afeb300a9. Report an issue: GitHub.