astrid-runtime/astrid · error

daemon workspace metadata was not available within {timeout_

Error message

daemon workspace metadata was not available within {timeout_secs} seconds; run `astrid restart`

What it means

After ensuring a daemon is up, the CLI verifies the daemon's workspace matches the caller's workspace by polling the readiness file, which carries daemon workspace metadata/fingerprints. If the file never appears (or stays unreadable as NotFound) within the default ready timeout, this error is raised. It means the daemon never finished booting far enough to publish its workspace metadata.

Source

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

    // Default wait only: do not load operator config here. Every admin
    // command including `agent list --format json` hits this path after
    // logging is live; Config::load_with_layout would trace to stderr and
    // poison merged stdout JSON in the crash-recovery smoke.
    let timeout_secs = default_daemon_ready_secs();
    let attempts = readiness_attempts(timeout_secs, ready::DAEMON_READY_POLL_MILLIS);
    for _ in 0..attempts {
        match std::fs::read_to_string(&ready_path) {
            Ok(metadata) => return validate_daemon_workspace_metadata(&metadata, &expected),
            Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
                tokio::time::sleep(DAEMON_READY_POLL).await;
            },
            Err(error) => {
                return Err(error).context("failed to read daemon workspace metadata");
            },
        }
    }

    anyhow::bail!(
        "daemon workspace metadata was not available within {timeout_secs} seconds; run `astrid restart`"
    )
}

/// Spawn a persistent (non-ephemeral) daemon and wait for readiness.
pub(crate) async fn spawn_persistent_daemon() -> Result<()> {
    let ready_path = socket_client::readiness_path();
    println!(
        "{}",
        theme::Theme::info("Starting Astrid daemon (persistent mode)...")
    );
    let ws = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
    let daemon_bin = find_companion_binary("astrid-daemon")?;

    let mut cmd = std::process::Command::new(daemon_bin);
    // No --ephemeral flag = persistent mode
    cmd.env(
        "ASTRID_WORKSPACE_STATE_DIR",

View on GitHub (pinned to affd8760f4)

Solutions

  1. Run `astrid restart` as the message advises, then retry the command.
  2. Check the daemon boot log to see whether it failed or is still importing; increase the ready timeout if the import is legitimately slow.
  3. Confirm no other daemon for a different workspace is bound to the endpoint; stop it and start the daemon for the intended workspace.
  4. Verify the readiness file directory exists and is writable (socket/readiness path configuration) if boot logs look healthy.

Example fix

# before
$ astrid agent list
Error: daemon workspace metadata was not available within 30 seconds; run `astrid restart`
# after
$ astrid restart
$ astrid agent list  # OK
Defensive patterns

Strategy: retry

Validate before calling

// Poll readiness metadata yourself before issuing commands
async fn wait_ready_metadata(ready_path: &Path, secs: u64) -> bool {
    for _ in 0..secs * 4 {
        if let Ok(meta) = std::fs::read_to_string(ready_path) {
            return validate_daemon_workspace_metadata(&meta, &expected).is_ok();
        }
        tokio::time::sleep(Duration::from_millis(250)).await;
    }
    false
}

Try / catch

match ensure_daemon_workspace_matches(workspace_root).await {
    Err(e) if e.to_string().contains("workspace metadata was not available") => {
        eprintln!("daemon never published workspace metadata; restarting");
        astrid_restart().await?;
        ensure_daemon_workspace_matches(workspace_root).await
    },
    other => other,
}

Prevention

When it happens

Trigger: ensure_daemon_inner_locked (after spawn or on UseExisting) or handle_start_locked calling ensure_daemon_workspace_matches, and the readiness file is still absent after readiness_attempts(default_daemon_ready_secs()) polls — daemon boot stalled, failed, or a live daemon from another workspace never writes the expected file.

Common situations: Daemon crashed mid-boot after the socket connected; a pre-existing daemon serving a different workspace whose metadata never satisfies validation; extremely slow first cutover exceeding the default ready timeout; readiness file deleted or on a flaky mount.

Understand the failure class

Related errors


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