astrid-runtime/astrid · error

FUSE service control endpoint is already live

Error message

FUSE service control endpoint is already live

What it means

bind_control_listener creates a fresh private Unix-socket control endpoint for the FUSE service and refuses to silently replace a live one. If the socket path already exists and a connection succeeds, another service instance is listening, so binding aborts to prevent hijacking an existing service's control channel.

Solutions

  1. Stop the already-running FUSE service instance (or its supervisor unit) before relaunching
  2. If no instance should exist, find and kill the leftover process holding the socket
  3. Use a distinct control socket path for the new instance
  4. Only if the connect succeeded spuriously, remove the socket file and retry — the code already removes truly stale (unconnectable) sockets automatically

Example fix

// before
run_launch("fuse-service.sock") // second instance
// after
if control_endpoint_live("fuse-service.sock") { stop_existing_instance()?; }
run_launch("fuse-service.sock")
Defensive patterns

Strategy: try-catch

Validate before calling

fn control_endpoint_live(path: &Path) -> bool { path.symlink_metadata().is_ok() && UnixStream::connect(path).is_ok() }

Try / catch

match run_launch(&opts).await { Err(e) if e.to_string().contains("already live") => { stop_existing_instance()?; run_launch(&opts).await } other => other }

Prevention

When it happens

Trigger: bind_control_listener (called from run_launch) finds that symlink_metadata(path) exists and UnixStream::connect(path) succeeds — meaning a previous/other FUSE service instance is still running and bound to that socket path.

Common situations: Starting a second instance of the FUSE service while one is already running; a stale-looking socket that actually still has a live listener; a supervisor restarting the service without stopping the old one first.

Related errors


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

Appendix: source

Thrown at crates/astrid-storage-provider-fuse/src/control.rs:126

        /// Access class retained by the service.
        access: StorageProviderAccessV1,
    },
    /// Operation completed.
    Done,
    /// Service refused the request.
    Failure {
        /// Stable local error code.
        code: String,
        /// Bounded diagnostic.
        message: String,
    },
}

/// Bind a fresh private control socket, refusing to replace a live service.
pub(crate) fn bind_control_listener(path: &Path) -> Result<tokio::net::UnixListener> {
    if path.symlink_metadata().is_ok() {
        if UnixStream::connect(path).is_ok() {
            bail!("FUSE service control endpoint is already live");
        }
        std::fs::remove_file(path).with_context(|| {
            format!(
                "remove stale FUSE service control socket {}",
                path.display()
            )
        })?;
    }
    if let Some(parent) = path.parent() {
        astrid_core::platform_fs::ensure_private_directory(parent)?;
    }
    let listener = StdUnixListener::bind(path)
        .with_context(|| format!("bind FUSE service control socket {}", path.display()))?;
    std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600))?;
    tokio::net::UnixListener::from_std(listener)
        .context("convert FUSE control listener to the async runtime")
}

View on GitHub (pinned to affd8760f4)