astrid-runtime/astrid · error

FSKit callback response exceeds the bounded frame size

Error message

FSKit callback response exceeds the bounded frame size

What it means

read_callback_response first reads a 4-byte big-endian length and rejects frames whose length is 0 or greater than MAX_CALLBACK_BYTES. This bound prevents allocating unbounded memory from a hostile or corrupt peer on the local callback socket. A response outside the bound aborts the probe.

Source

Thrown at crates/astrid-storage-provider-fskit/src/service.rs:247

    stream.flush().await?;
    let response = read_callback_response(&mut stream).await?;
    if response.request_id != request.request_id {
        bail!("FSKit callback probe correlation mismatch");
    }
    match response.outcome {
        StorageFilesystemOutcomeV2::Success(_) => Ok(()),
        StorageFilesystemOutcomeV2::Failure(StorageFilesystemFailureV1 { code, message }) => {
            bail!("FSKit callback probe failed [{code}]: {message}")
        },
    }
}

async fn read_callback_response(stream: &mut LocalStream) -> Result<StorageFilesystemResponseV2> {
    let mut length = [0_u8; 4];
    stream.read_exact(&mut length).await?;
    let length = u32::from_be_bytes(length) as usize;
    if length == 0 || length > MAX_CALLBACK_BYTES {
        bail!("FSKit callback response exceeds the bounded frame size");
    }
    let mut bytes = vec![0_u8; length];
    stream.read_exact(&mut bytes).await?;
    let response: StorageFilesystemResponseV2 =
        serde_json::from_slice(&bytes).context("decode FSKit callback probe")?;
    if response.protocol_version != STORAGE_FILESYSTEM_PROTOCOL_V2 {
        bail!("FSKit callback probe protocol mismatch");
    }
    Ok(response)
}

async fn service_loop(
    listener: &LocalListener,
    launch: &StorageProviderServiceLaunchV1,
    mounted: &mut bool,
) -> Result<()> {
    let mut poll = tokio::time::interval(SERVICE_POLL);
    loop {

View on GitHub (pinned to affd8760f4)

Solutions

  1. Verify the responder writes a 4-byte big-endian u32 length before the JSON payload
  2. Ensure each response is written on a fresh/synchronized stream so framing stays aligned
  3. Check the payload size against MAX_CALLBACK_BYTES and shrink large responses
  4. Confirm no stale process is bound to the control socket; restart the service

Example fix

// before: little-endian length on responder side
try length.withUnsafeBytes { Data($0) } // LE
// after
var be = length.bigEndian
try be.withUnsafeBytes { stream.write(Data($0)) }
Defensive patterns

Strategy: validation

Validate before calling

fn frame_len_ok(len: usize, max: usize) -> bool { len > 0 && len <= max }

Try / catch

match read_callback_response(&mut stream).await { Err(e) if e.to_string().contains("bounded frame size") => { mark_stream_desynced(); restart_responder(); } other => other }

Prevention

When it happens

Trigger: read_callback_response (called by probe_callback) reads a length prefix that is 0 (empty frame) or exceeds MAX_CALLBACK_BYTES — e.g. the peer wrote garbage, desynchronized the framing, or sent an oversized JSON blob.

Common situations: A stream desync after a previous short read; an extension writing the length in little-endian or as text instead of 4-byte BE; a malicious/stale process connected to the socket.

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