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

  1. Upgrade or restart the callback peer so both sides use STORAGE_FILESYSTEM_PROTOCOL_V2.
  2. Ensure only one provider instance is running and no stale binaries from an older install are on PATH.
  3. Check for leftover sockets/streams from previous launches and clean them up before relaunching.
  4. 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

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


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)