zeroclaw-labs/zeroclaw · error · anyhow::Error

`zeroclaw sop approve/deny/pending` requires the agent-runti

Error message

`zeroclaw sop approve/deny/pending` requires the agent-runtime build (the gateway client)

What it means

`zeroclaw sop approve|deny|pending` must reach the running daemon's `/admin/sop/*` endpoints, and the gateway HTTP client plus `gateway_admin_url` compile only behind the `agent-runtime` cargo feature. In builds without it, `sop_admin_dispatch` bails immediately instead of failing at connect time. Local SOP verbs (list/validate/show) still work because they never touch the gateway.

Source

Thrown at src/main.rs:6848

            Err(anyhow::Error::msg(format!(
                "Failed to connect to gateway: {e}"
            )))
        }
    }
}

/// Dispatch the gateway-backed SOP verbs. Requires the `agent-runtime` build (the
/// gateway HTTP client + `gateway_admin_url` live behind it, like `shutdown_gateway`);
/// without it these verbs cannot reach the daemon, so they error clearly.
async fn sop_admin_dispatch(cmd: SopCommands, config: &crate::config::Config) -> Result<()> {
    #[cfg(feature = "agent-runtime")]
    {
        sop_admin_request(cmd, config).await
    }
    #[cfg(not(feature = "agent-runtime"))]
    {
        let _ = (cmd, config);
        anyhow::bail!(
            "`zeroclaw sop approve/deny/pending` requires the agent-runtime build (the gateway client)"
        )
    }
}

/// CLI -> daemon dispatch for the out-of-band SOP approval verbs (EPIC C, C8).
/// Posts to `/admin/sop/*` on the running gateway (mirrors `shutdown_gateway`);
/// never builds a throwaway local engine, which cannot see the daemon's runs.
#[cfg(feature = "agent-runtime")]
async fn sop_admin_request(cmd: SopCommands, config: &crate::config::Config) -> Result<()> {
    let host = config.gateway.host.clone();
    let port = config.gateway.port;
    let prefix = config.gateway.path_prefix.as_deref();
    let client = reqwest::Client::new();
    match cmd {
        SopCommands::Pending => {
            let url = gateway_admin_url(&host, port, prefix, "/admin/sop/pending");
            let resp = client

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Use the standard release binary — agent-runtime is part of the default feature set
  2. Build from source with the feature: `cargo build --release` (default features) or `--features agent-runtime`
  3. If the binary cannot be swapped, drive the daemon directly: GET/POST `http://<gateway.host>:<gateway.port>[<path_prefix>]/admin/sop/{pending,approve,deny}`

Example fix

# before
cargo build --release --no-default-features
zeroclaw sop pending   # bails: needs agent-runtime
# after
cargo build --release   # default features include agent-runtime
zeroclaw sop pending
Defensive patterns

Strategy: fallback

Validate before calling

# smoke-test the binary's capability before scripting SOP approvals
out="$(zeroclaw sop pending 2>&1)" || case "$out" in
  *agent-runtime*) echo "slim build: route SOP admin via HTTP instead"; exit 3 ;;
  *) echo "$out"; exit 1 ;;
esac

Prevention

When it happens

Trigger: Running `zeroclaw sop pending|approve|deny` on a binary built with `--no-default-features` or otherwise without `agent-runtime` (e.g. slim distro or downstream packaging).

Common situations: Distro/container images that trim cargo features to shrink the binary; custom `cargo build` invocations copying a minimal feature list; a slim CLI paired with a full daemon.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/05de1d94b1feb37c. Report an issue: GitHub.