astrid-runtime/astrid · error

FUSE service control endpoint is already present

Error message

FUSE service control endpoint is already present

What it means

validate_control_path finishes by checking, via local_transport::endpoint_is_present, that no control endpoint (Unix socket) already exists at the control path. A pre-existing endpoint means another service instance owns or leaked the socket, so a new launch would either fail to bind or hijack the peer's channel.

Solutions

  1. Stop the already-running service instance that owns the socket, or wait for it to exit, before launching
  2. Remove the stale socket file (it is inside resource_path, deleted with the lease directory) if no process is listening
  3. Use a unique resource_path per launch so endpoints never collide
  4. Add supervisor logic that cleans the lease directory after abnormal termination

Example fix

# before: stale socket from crashed instance
$ ss -x | grep process-control   # confirm no listener
$ rm /run/astrid/leases/<id>/process-control.sock
# then relaunch
Defensive patterns

Strategy: validation

Validate before calling

if std::path::Path::new(control_path).exists() {
    // confirm no live listener, then remove stale socket before launching
}

Prevention

When it happens

Trigger: Calling validate_launch when a socket file already exists and is live at <resource_path>/process-control.sock — i.e. another FUSE service is running or a stale socket file was left behind.

Common situations: Double-starting the service (systemd restart overlapping, two supervisor processes); a crashed prior instance leaving the socket file on disk; reusing the same resource_path/launch directory for multiple concurrent launches.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/2b6f63f0eba815b0. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-storage-provider-fuse/src/service.rs:292

            .components()
            .any(|component| matches!(component, std::path::Component::ParentDir))
    {
        bail!("FUSE service control path is malformed");
    }
    if control_path != resource_path.join("process-control.sock") {
        bail!("FUSE service control path is not the kernel endpoint");
    }
    let parent = control_path
        .parent()
        .context("FUSE service control path has no parent")?;
    platform_fs::validate_private_directory(parent)
        .context("validate private FUSE control parent")?;
    platform_fs::verify_no_redirects(control_path)
        .context("reject redirected FUSE control path")?;
    if local_transport::endpoint_is_present(control_path)
        .context("inspect FUSE service control endpoint")?
    {
        bail!("FUSE service control endpoint is already present");
    }
    Ok(())
}

async fn probe_callback(launch: &StorageProviderServiceLaunchV1) -> Result<()> {
    let mut stream = local_transport::connect(&launch.lease.callback_path)
        .await
        .context("connect FUSE lease callback")?;
    let request = StorageFilesystemRequestV2 {
        protocol_version: STORAGE_FILESYSTEM_PROTOCOL_V2,
        request_id: format!("fuse-service-{}", Uuid::new_v4()),
        lease_token: launch.lease.lease_token.clone(),
        operation: StorageFilesystemOperationV2::Stat {
            path: String::new(),
        },
    };
    let bytes = serde_json::to_vec(&request).context("encode FUSE callback probe")?;
    let length = u32::try_from(bytes.len()).context("FUSE callback probe is too large")?;

View on GitHub (pinned to affd8760f4)