astrid-runtime/astrid · error

failed to acquire daemon start fence: {error}

Error message

failed to acquire daemon start fence: {error}

What it means

Thrown by `acquire_daemon_start_fence` when the file-lock used as a daemon start fence (preventing concurrent daemon startup for the same home) cannot be acquired. The inner lock result is mapped into this error, so the concrete cause ({error}) is the underlying lock API's error — typically the lock being held elsewhere or an OS-level lock failure. Called from ensure_daemon_inner, handle_start, and handle_stop.

Source

Thrown at crates/astrid-cli/src/commands/daemon.rs:461

            {
                use std::os::unix::fs::PermissionsExt;
                std::fs::set_permissions(parent, std::fs::Permissions::from_mode(0o700))?;
            }
        }
        let mut options = std::fs::OpenOptions::new();
        options.read(true).write(true).create(true).truncate(false);
        #[cfg(unix)]
        {
            use std::os::unix::fs::OpenOptionsExt;
            options.mode(0o600);
        }
        let file = options.open(&path)?;
        file.lock()?;
        Ok(file)
    })
    .await
    .context("daemon start fence task failed")?
    .map_err(|error| anyhow::anyhow!("failed to acquire daemon start fence: {error}"))?;
    Ok(Arc::new(file))
}

/// Admit a disposable runtime override before lifecycle code can resolve or
/// mutate any path under it.
pub(crate) fn validate_runtime_admission() -> Result<()> {
    let home = astrid_core::dirs::AstridHome::resolve()
        .context("failed to resolve Astrid home for runtime admission")?;
    home.validate_run_dir()
        .context("failed to validate ASTRID_RUN_DIR")
}

/// Keep the start fence outside the home until the kernel admits its layout.
fn daemon_start_fence_path(home: &astrid_core::dirs::AstridHome) -> std::path::PathBuf {
    let digest = blake3::hash(home.root().to_string_lossy().as_bytes());
    std::env::temp_dir()
        .join("astrid-start-fences")
        .join(format!("{digest}.lock"))

View on GitHub (pinned to affd8760f4)

Solutions

  1. Check whether another daemon is already running for this home (`astrid daemon status`) and don't start a second one.
  2. Identify the process holding the fence lock (`lsof`/`fuser` on the lock path under the home) and terminate it if stale.
  3. Wait and retry — the fence is held only transiently during start/stop.
  4. If the home is on NFS/unsupported fs, move ASTRID_HOME to a local filesystem.
Defensive patterns

Strategy: retry

Validate before calling

let lock_path = home.runtime_dir().join("daemon.start.lock");
if let Ok(holders) = std::fs::read_to_string(format!("/proc/locks")) {
    // or use lsof in shell preflight
}
// shell preflight: fuser "$ASTRID_HOME/runtime/daemon.start.lock" && echo "fence held"

Try / catch

match acquire_daemon_start_fence().await {
    Err(e) if e.to_string().contains("failed to acquire daemon start fence") => {
        tokio::time::sleep(Duration::from_millis(500)).await;
        acquire_daemon_start_fence().await // bounded retry
    }
    other => other,
}

Prevention

When it happens

Trigger: Running `astrid daemon start`/`stop` (or ensure_daemon) while another process holds the fence lock file for the same Astrid home, or when the lock file is on a filesystem that doesn't support the lock operation (e.g. some network mounts).

Common situations: Two terminals starting the daemon simultaneously; a previous daemon died leaving a lock held by a stuck process; ASTRID_HOME on NFS where flock semantics fail.

Related errors


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