zeroclaw-labs/zeroclaw · warning
update --check failed: {}
Error message
update --check failed: {} What it means
The gateway version check re-spawns the current executable as `zeroclaw update --check --json` and surfaces the subprocess's trimmed stderr verbatim when it exits non-zero. The real cause is in that stderr text — commonly network failure, an invalid `--version` argument, or an environment where the self-update check cannot run. The HTTP handler degrades gracefully (returns 200 with an `error` field), so this surfaces as a warning, not a failed request.
Source
Thrown at crates/zeroclaw-gateway/src/version.rs:218
}
async fn run_cli_check(version: Option<&str>) -> anyhow::Result<CliCheck> {
let exe = std::env::current_exe().context("cannot determine current executable path")?;
let mut cmd = tokio::process::Command::new(exe);
cmd.arg("update").arg("--check").arg("--json");
if let Some(v) = version {
cmd.arg("--version").arg(v);
}
cmd.stdin(std::process::Stdio::null());
let output = tokio::time::timeout(CHECK_TIMEOUT, cmd.output())
.await
.context("version check timed out")?
.context("failed to spawn `zeroclaw update --check`")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("update --check failed: {}", stderr.trim());
}
let stdout = String::from_utf8_lossy(&output.stdout);
serde_json::from_str::<CliCheck>(stdout.trim())
.context("failed to parse `update --check --json` output")
}
/// `GET /api/version/check[?force=true][&version=X]`
///
/// Never fails the dashboard: on any error it returns 200 with
/// `{ is_newer: false, error }` so the version tag degrades gracefully.
pub async fn handle_version_check(
State(state): State<AppState>,
headers: HeaderMap,
Query(q): Query<CheckQuery>,
) -> impl IntoResponse {
if let Err(e) = require_auth(&state, &headers) {
return e.into_response();View on GitHub (pinned to 88bb9c8533)
Solutions
- Run `zeroclaw update --check --json` manually and read the stderr reproduced in the message
- If a `version` query param is set, pass a valid semver string or omit it entirely
- Ensure outbound network/proxy access for the update check; the dashboard degrades gracefully, so this is non-urgent
Example fix
# before GET /api/version/check?version=latest # after GET /api/version/check GET /api/version/check?version=0.7.2
Defensive patterns
Strategy: fallback
Validate before calling
// Validate a caller-supplied version before it reaches the check:
fn is_semver_like(v: &str) -> bool {
!v.is_empty() && v.chars().next().is_some_and(|c| c.is_ascii_digit())
&& v.split('.').count() >= 2
} Try / catch
let info = match run_cli_check(version).await {
Ok(c) => c,
Err(e) => {
// Dashboard contract: never fail the request; degrade to a warning payload.
return VersionCheckInfo { is_newer: false, error: Some(e.to_string()), ..Default::default() };
}
}; Prevention
- Cache version-check results and avoid force=true on every dashboard load
- Validate any ?version= parameter as semver before passing it through
- Treat update-check failures as informational — never block gateway startup or UI on them
When it happens
Trigger: `GET /api/version/check` with `force=true` or a `version` query param that busts the cache, while `zeroclaw update --check` fails: offline machine, proxy blocking the release host, or a non-semver `version` value the update command cannot parse.
Common situations: Air-gapped or proxied environments; passing `?version=latest` instead of a semver string; running a dev build whose self-update metadata is missing.
Related errors
- QQ gateway request failed ({status}): {err}
- QQ WebSocket connection closed: invalid session (fresh auth
- QQ WebSocket connection closed: server requested reconnect (
- QQ WebSocket connection closed: close_code={code}, reason="{
- Edge TTS subprocess timed out
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/4ae1e2e6c754c67e.
Report an issue: GitHub.