astrid-runtime/astrid · error
WinFsp callback response exceeds limit
Error message
WinFsp callback response exceeds limit
What it means
probe_callback sends a length-prefixed callback probe over the local control transport and enforces a size cap of SERVICE_MAX_CALLBACK_BYTES (8 MiB). A response of length 0 or greater than 8 MiB is rejected as corrupt or malicious, since the daemon cannot trust arbitrarily large callback replies.
Solutions
- Fix the callback responder to frame replies with a correct big-endian u32 length in (1..=8 MiB] followed by the JSON body.
- Verify byte order (big-endian) in the responder's length prefix.
- Confirm nothing else is bound to callback_path and answering the probe.
- Reduce the payload returned by the callback if it legitimately exceeds 8 MiB.
Example fix
// before (responder) stream.write_all(&(bytes.len() as u32).to_le_bytes()).await?; // after stream.write_all(&(bytes.len() as u32).to_be_bytes()).await?;
Defensive patterns
Strategy: type-guard
Validate before calling
const SERVICE_MAX_CALLBACK_BYTES: usize = 8 * 1024 * 1024;
fn response_length_is_valid(len: u32) -> bool {
(len as usize) > 0 && (len as usize) <= SERVICE_MAX_CALLBACK_BYTES
} Type guard
fn is_valid_framed_response(bytes: &[u8]) -> bool {
bytes.len() >= 4 && {
let len = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize;
len > 0 && len <= 8 * 1024 * 1024 && bytes.len() - 4 == len
}
} Try / catch
match probe_callback(&launch, &request).await {
Err(e) if e.to_string().contains("exceeds limit") => {
log::error!("callback responder framing bug (big-endian u32, <=8MiB)");
Err(e)
},
other => other,
} Prevention
- Frame responses with a big-endian u32 length prefix kept under 8 MiB.
- Dedicate the callback socket to a single known responder process.
- Add a responder integration test asserting the framing matches the daemon's expectations.
When it happens
Trigger: run_private_service -> probe_callback when the callback responder writes a zero-length response, or writes a 4-byte big-endian length exceeding 8 MiB before the JSON body.
Common situations: A buggy or foreign responder on callback_path returning garbage bytes; a protocol where the length field is little-endian (making huge u32 values); a compromised or misbehaving local process answering the probe; truncated framing swapping length/body order.
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
- WinFsp callback probe correlation mismatch
- WinFsp callback probe protocol mismatch
- WinFsp daemon returned an invalid stop acknowledgement
- WinFsp service control request exceeds limit
- build WinFsp callback filesystem
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/4dce419bc946c011.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-storage-provider-winfsp/src/win.rs:359
.context("connect WinFsp lease callback")?;
let request = StorageFilesystemRequestV2 {
protocol_version: STORAGE_FILESYSTEM_PROTOCOL_V2,
request_id: format!("winfsp-service-{}", launch.lease.mount_id),
lease_token: launch.lease.lease_token.clone(),
operation: StorageFilesystemOperationV2::Stat {
path: String::new(),
},
};
let bytes = serde_json::to_vec(&request).context("encode WinFsp callback probe")?;
let length = u32::try_from(bytes.len()).context("WinFsp callback probe is too large")?;
stream.write_all(&length.to_be_bytes()).await?;
stream.write_all(&bytes).await?;
stream.flush().await?;
let mut response_length = [0_u8; 4];
stream.read_exact(&mut response_length).await?;
let length = u32::from_be_bytes(response_length) as usize;
if length == 0 || length > SERVICE_MAX_CALLBACK_BYTES {
bail!("WinFsp callback response exceeds limit");
}
let mut response_bytes = vec![0_u8; length];
stream.read_exact(&mut response_bytes).await?;
let response: StorageFilesystemResponseV2 =
serde_json::from_slice(&response_bytes).context("decode WinFsp callback probe")?;
if response.protocol_version != STORAGE_FILESYSTEM_PROTOCOL_V2 {
bail!("WinFsp callback probe protocol mismatch");
}
if response.request_id != request.request_id {
bail!("WinFsp callback probe correlation mismatch");
}
match response.outcome {
StorageFilesystemOutcomeV2::Success(_) => Ok(()),
StorageFilesystemOutcomeV2::Failure(StorageFilesystemFailureV1 { code, message }) => {
bail!("WinFsp callback probe failed [{code}]: {message}")
},
}
}View on GitHub (pinned to affd8760f4)