{"record":{"id":"bf515118c6e0f6b9","repo":"astrid-runtime/astrid","slug":"fuse-callback-probe-protocol-mismatch","errorCode":null,"errorMessage":"FUSE callback probe protocol mismatch","messagePattern":"FUSE callback probe protocol mismatch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-fuse/src/service.rs","lineNumber":338,"sourceCode":"        StorageFilesystemOutcomeV2::Failure(StorageFilesystemFailureV1 { code, message }) => {\n            bail!(\"FUSE callback probe failed [{code}]: {message}\")\n        },\n    }\n}\n\nasync fn read_callback_response(stream: &mut LocalStream) -> Result<StorageFilesystemResponseV2> {\n    let mut length = [0_u8; 4];\n    stream.read_exact(&mut length).await?;\n    let length = u32::from_be_bytes(length) as usize;\n    if length == 0 || length > MAX_CALLBACK_BYTES {\n        bail!(\"FUSE callback response exceeds the bounded frame size\");\n    }\n    let mut bytes = vec![0_u8; length];\n    stream.read_exact(&mut bytes).await?;\n    let response: StorageFilesystemResponseV2 =\n        serde_json::from_slice(&bytes).context(\"decode FUSE callback probe\")?;\n    if response.protocol_version != STORAGE_FILESYSTEM_PROTOCOL_V2 {\n        bail!(\"FUSE callback probe protocol mismatch\");\n    }\n    Ok(response)\n}\n\n#[cfg(unix)]\nfn parent_is_alive(\n    parent: &astrid_core::storage_filesystem::StorageProviderParentLifetimeV1,\n) -> bool {\n    use nix::sys::signal::kill;\n    use nix::unistd::Pid;\n\n    let Ok(pid) = i32::try_from(parent.pid) else {\n        return false;\n    };\n    if !matches!(\n        kill(Pid::from_raw(pid), None),\n        Ok(()) | Err(nix::errno::Errno::EPERM)\n    ) {","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-fuse/src/service.rs#L320-L356","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: mixed versions on the stream\nlet response = read_callback_response(stream).await?;\n// after: negotiate/diagnose before decoding\nlet raw = read_raw_frame(stream).await?;\nlet version = peek_protocol_version(&raw)?;\nif version != STORAGE_FILESYSTEM_PROTOCOL_V2 {\n    bail!(\"callback peer speaks protocol {version}, expected {STORAGE_FILESYSTEM_PROTOCOL_V2}; upgrade the peer\");\n}","handlingStrategy":"type-guard","validationCode":"// Before dispatching to the provider, check deployed binary versions agree:\n// provider_version == kernel_expected_provider_version","typeGuard":"// Rust\nfn is_v2(r: &StorageFilesystemResponseV2) -> bool {\n    r.protocol_version == STORAGE_FILESYSTEM_PROTOCOL_V2\n}","tryCatchPattern":"// Rust\nmatch launch().await {\n    Err(e) if e.to_string().contains(\"protocol mismatch\") => {\n        // upgrade/restart the peer, then retry once\n    }\n    other => other?,\n}","preventionTips":["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"],"tags":["fuse","protocol-version","version-mismatch","ipc"],"backgroundTag":"protocol-version-mismatch","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}