astrid-runtime/astrid · error
FUSE callback probe protocol mismatch
Error message
FUSE callback probe protocol mismatch
What it means
read_callback_response() decoded a StorageFilesystemResponseV2 from the callback stream, but its protocol_version does not equal STORAGE_FILESYSTEM_PROTOCOL_V2. The provider bails to avoid acting on a response whose semantics it does not share.
Solutions
- Upgrade or restart the callback peer so both sides use STORAGE_FILESYSTEM_PROTOCOL_V2.
- Ensure only one provider instance is running and no stale binaries from an older install are on PATH.
- Check for leftover sockets/streams from previous launches and clean them up before relaunching.
- Pin provider and kernel/package versions together when deploying.
Example fix
// before: mixed versions on the stream
let response = read_callback_response(stream).await?;
// after: negotiate/diagnose before decoding
let raw = read_raw_frame(stream).await?;
let version = peek_protocol_version(&raw)?;
if version != STORAGE_FILESYSTEM_PROTOCOL_V2 {
bail!("callback peer speaks protocol {version}, expected {STORAGE_FILESYSTEM_PROTOCOL_V2}; upgrade the peer");
} Defensive patterns
Strategy: type-guard
Validate before calling
// Before dispatching to the provider, check deployed binary versions agree: // provider_version == kernel_expected_provider_version
Type guard
// Rust
fn is_v2(r: &StorageFilesystemResponseV2) -> bool {
r.protocol_version == STORAGE_FILESYSTEM_PROTOCOL_V2
} Try / catch
// Rust
match launch().await {
Err(e) if e.to_string().contains("protocol mismatch") => {
// upgrade/restart the peer, then retry once
}
other => other?,
} Prevention
- Deploy provider and kernel binaries atomically so versions never diverge
- Kill stale provider processes and remove old sockets before relaunching
- Pin versions in deployment manifests
When it happens
Trigger: probe_callback() calls read_callback_response() and the peer answers with a StorageFilesystemResponseV2 whose protocol_version field is not STORAGE_FILESYSTEM_PROTOCOL_V2 (older or newer provider/kernel build on the other end of the LocalStream).
Common situations: Mixed-version deployment after an upgrade: the FUSE provider binary was updated but the callback peer (or vice versa) was not; a stale provider process from a previous install still owns the stream; custom builds with divergent protocol constants.
Related errors
- detached FUSE service did not retain its control endpoint
- detached FUSE service exceeded the startup response size
- FUSE callback path is not the kernel lease endpoint
- FUSE callback probe correlation mismatch
- FUSE callback response exceeds the bounded frame size
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/bf515118c6e0f6b9.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-storage-provider-fuse/src/service.rs:338
StorageFilesystemOutcomeV2::Failure(StorageFilesystemFailureV1 { code, message }) => {
bail!("FUSE 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!("FUSE 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 FUSE callback probe")?;
if response.protocol_version != STORAGE_FILESYSTEM_PROTOCOL_V2 {
bail!("FUSE callback probe protocol mismatch");
}
Ok(response)
}
#[cfg(unix)]
fn parent_is_alive(
parent: &astrid_core::storage_filesystem::StorageProviderParentLifetimeV1,
) -> bool {
use nix::sys::signal::kill;
use nix::unistd::Pid;
let Ok(pid) = i32::try_from(parent.pid) else {
return false;
};
if !matches!(
kill(Pid::from_raw(pid), None),
Ok(()) | Err(nix::errno::Errno::EPERM)
) {View on GitHub (pinned to affd8760f4)