astrid-runtime/astrid · error
FUSE service returned an incompatible unmount response
Error message
FUSE service returned an incompatible unmount response
What it means
The unmount flow expects ControlResponse::Done (or a Failure) from the FUSE control service. Receiving a ControlResponse::Status instead means the service answered an unmount request with status information, which the client treats as incompatible and bails (crates/astrid-storage-provider-fuse/src/main.rs:878).
Source
Thrown at crates/astrid-storage-provider-fuse/src/main.rs:878
&record.mountpoint,
record.auto_created_mountpoint,
)?;
Ok(())
}
fn control_unmount(control_path: &Path, acting_principal: &astrid_core::PrincipalId) -> Result<()> {
match call_control(
control_path,
&ControlRequest::Unmount {
requested_by: acting_principal.clone(),
},
)? {
ControlResponse::Done => Ok(()),
ControlResponse::Failure { code, message } => {
bail!("FUSE service unmount failed [{code}]: {message}")
},
ControlResponse::Status { .. } => {
bail!("FUSE service returned an incompatible unmount response")
},
}
}
fn cleanup_mountpoint(mountpoint: &Path, auto_created: bool) -> Result<()> {
if auto_created
&& !mountpoint::mountinfo_contains(mountpoint)?
&& std::fs::symlink_metadata(mountpoint).is_ok_and(|metadata| metadata.is_dir())
&& std::fs::read_dir(mountpoint)?.next().is_none()
{
let _ = std::fs::remove_dir(mountpoint);
}
Ok(())
}
fn lease_from_response(body: AdminResponseBody) -> Result<StorageMountLeaseV1> {
match body {
AdminResponseBody::StorageMountLease(lease) => Ok(*lease),View on GitHub (pinned to affd8760f4)
Solutions
- Verify the process bound to the control socket is the correct astrid FUSE service version and restart it if stale
- Remove stale socket files and retry the unmount
- Upgrade client and service together so ControlResponse variants line up
Example fix
// before
send_control(socket, ControlRequest::Unmount{..})?; // got Status back
// after
assert_control_service_version(socket)?; // confirm peer speaks same protocol
send_control(socket, ControlRequest::Unmount{..})?; Defensive patterns
Strategy: type-guard
Validate before calling
let ok = matches!(resp, ControlResponse::Done | ControlResponse::Failure { .. }); Type guard
fn is_unmount_response(resp: &ControlResponse) -> bool { matches!(resp, ControlResponse::Done | ControlResponse::Failure { .. }) } Try / catch
match unmount(...) { Err(e) if e.to_string().contains("incompatible unmount response") => { restart_service_and_retry()? }, r => r? } Prevention
- Verify the control socket peer before sending requests
- Keep client/service protocol versions in sync
- Restart stale services before lifecycle operations
When it happens
Trigger: Sending ControlRequest::Unmount and getting a Status response — indicating the peer is not the expected control service or the request was misinterpreted (protocol/version mismatch).
Common situations: A stale or foreign process listening on the control socket; client/service version skew where Unmount handling changed; racing reconnect to a service that just restarted.
Related errors
- registered FUSE service returned an incompatible status resp
- unexpected capsule metadata response: {other:?}
- unexpected response from kernel: {body:?}
- unexpected response from kernel: {other:?}
- kernel returned an unexpected storage unmount response
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/3e120b1064e22252.
Report an issue: GitHub.