astrid-runtime/astrid · error

mount callback frame exceeds limit

Error message

mount callback frame exceeds limit

What it means

The mount IPC callback protocol frames each request with a big-endian u32 length; a frame declaring a length above MAX_CALLBACK_FRAME_BYTES is rejected before any body is read. This prevents a malformed or malicious peer from making the server allocate an unbounded buffer.

Solutions

  1. Ensure the client and server are the same version so both agree on frame limits and framing
  2. Restart both ends of the mount connection to resynchronize the stream
  3. Check what is actually connecting to the callback socket — reject/fix rogue or wrong clients
  4. If limits legitimately changed, raise MAX_CALLBACK_FRAME_BYTES consistently on both sides
Defensive patterns

Strategy: validation

Validate before calling

// client side: check the frame before writing it
if payload.len() > MAX_CALLBACK_FRAME_BYTES {
    return Err("callback frame too large; split or reject");
}

Try / catch

match err.downcast_ref::<io::Error>() {
    Some(e) if e.kind() == io::ErrorKind::InvalidData && msg.contains("frame exceeds limit") => {
        // resynchronize: drop the connection and reconnect with matching framing
        reconnect_mount()?;
    }
    _ => return Err(err),
}

Prevention

When it happens

Trigger: `read_request` (called by `handle_connection`) reads the 4-byte length prefix, converts it to usize, and finds `length > MAX_CALLBACK_FRAME_BYTES`. Caused by protocol desync (garbage bytes interpreted as a length), a buggy/incompatible client version, or a corrupted stream.

Common situations: Mixing client and server versions with different framing; connecting a wrong process/socket to the mount callback port; binary garbage after an earlier partial read desynchronized framing.

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/233f5ba8b805324b. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-kernel/src/storage_mount.rs:576

        };
        let response = dispatch_request(&kernel, &state, request).await;
        if write_response(&mut stream, response).await.is_err() {
            return;
        }
    }
}

#[cfg(any(unix, windows))]
async fn read_request(stream: &mut LocalStream) -> Result<Option<CallbackRequest>, io::Error> {
    let mut length = [0_u8; 4];
    match stream.read_exact(&mut length).await {
        Ok(_) => {},
        Err(error) if error.kind() == io::ErrorKind::UnexpectedEof => return Ok(None),
        Err(error) => return Err(error),
    }
    let length = u32::from_be_bytes(length) as usize;
    if length > MAX_CALLBACK_FRAME_BYTES {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "mount callback frame exceeds limit",
        ));
    }
    let mut bytes = vec![0_u8; length];
    stream.read_exact(&mut bytes).await?;
    let protocol = serde_json::from_slice::<ProtocolProbe>(&bytes)
        .map_err(io::Error::other)?
        .protocol_version;
    if protocol == STORAGE_FILESYSTEM_PROTOCOL_V2 {
        let request = serde_json::from_slice::<StorageFilesystemRequestV2>(&bytes)
            .map_err(io::Error::other)?;
        let operation = decode_operation_v2(request.operation)?;
        Ok(Some(CallbackRequest {
            request: StorageFilesystemRequestV1 {
                protocol_version: STORAGE_FILESYSTEM_PROTOCOL_V1,
                request_id: request.request_id,
                lease_token: request.lease_token,

View on GitHub (pinned to affd8760f4)