{"record":{"id":"4072a2d77618262e","repo":"astrid-runtime/astrid","slug":"fskit-callback-probe-correlation-mismatch","errorCode":null,"errorMessage":"FSKit callback probe correlation mismatch","messagePattern":"FSKit callback probe correlation mismatch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage-provider-fskit/src/service.rs","lineNumber":232,"sourceCode":"    let mut stream = local_transport::connect(&launch.lease.callback_path)\n        .await\n        .context(\"connect FSKit lease callback\")?;\n    let request = StorageFilesystemRequestV2 {\n        protocol_version: STORAGE_FILESYSTEM_PROTOCOL_V2,\n        request_id: format!(\"fskit-service-{}\", launch.lease.mount_id),\n        lease_token: launch.lease.lease_token.clone(),\n        operation: StorageFilesystemOperationV2::Stat {\n            path: String::new(),\n        },\n    };\n    let bytes = serde_json::to_vec(&request).context(\"encode FSKit callback probe\")?;\n    let length = u32::try_from(bytes.len()).context(\"FSKit callback probe is too large\")?;\n    stream.write_all(&length.to_be_bytes()).await?;\n    stream.write_all(&bytes).await?;\n    stream.flush().await?;\n    let response = read_callback_response(&mut stream).await?;\n    if response.request_id != request.request_id {\n        bail!(\"FSKit callback probe correlation mismatch\");\n    }\n    match response.outcome {\n        StorageFilesystemOutcomeV2::Success(_) => Ok(()),\n        StorageFilesystemOutcomeV2::Failure(StorageFilesystemFailureV1 { code, message }) => {\n            bail!(\"FSKit 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!(\"FSKit callback response exceeds the bounded frame size\");\n    }\n    let mut bytes = vec![0_u8; length];\n    stream.read_exact(&mut bytes).await?;","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage-provider-fskit/src/service.rs#L214-L250","documentation":"The FSKit storage provider sends a callback probe over a local Unix socket with a request_id and expects a response carrying the same id. If the response's request_id differs from the one sent, the transport is returning answers for a different request, so the provider aborts the probe rather than misattributing an outcome. This guards against cross-request response confusion in the multiplexed callback channel.","triggerScenarios":"probe_callback (called from run) writes a length-prefixed JSON probe then calls read_callback_response; the returned StorageFilesystemResponseV2 has a request_id != request.request_id — i.e. the responder echoed a stale or wrong id, responses were reordered/interleaved, or a previous probe's late answer was consumed by this read.","commonSituations":"A buggy or older FSKit extension that doesn't echo request ids; two probes racing on the same socket; a responder that crashed and a leftover buffered response being drained.","solutions":["Verify the FSKit extension echoes the exact request_id from StorageFilesystemRequestV2 in every response","Ensure each probe uses a fresh socket/connection so responses cannot interleave","Check that request.request_id is unique per probe (e.g. monotonically increasing) and not reused","Retry the probe once; if mismatches persist, log both ids and report the extension version"],"exampleFix":"// before: reusing one stream for concurrent probes\nlet response = read_callback_response(&mut shared_stream).await?;\n// after: give each probe its own connection\nlet (mut stream, _) = listener.accept().await?;\nlet response = read_callback_response(&mut stream).await?;\nassert_eq!(response.request_id, request.request_id);","handlingStrategy":"validation","validationCode":"fn probe_ids_match(sent: &StorageFilesystemRequestV2, resp: &StorageFilesystemResponseV2) -> bool { resp.request_id == sent.request_id }","typeGuard":null,"tryCatchPattern":"match probe_callback(&req).await { Err(e) if e.to_string().contains(\"correlation mismatch\") => retry_probe_once(&req).await, other => other }","preventionTips":["Use one socket/connection per probe","Generate unique, monotonically increasing request ids","Don't reuse request ids across retries without draining old responses","Log sent vs received request ids to catch responder bugs early"],"tags":["ipc","protocol","concurrency"],"backgroundTag":"unexpected-response-shape","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"}