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

plugin registry returned HTTP {status} for {registry_url}

Error message

plugin registry returned HTTP {status} for {registry_url}

What it means

The plugin registry index fetch succeeded at the transport level but returned a non-success HTTP status. This is the generic branch: any status other than 2xx that is not the special-cased default-URL 404 (error 1406) bails here with the concrete status code and URL.

Source

Thrown at src/plugin_registry.rs:69

        || source.contains('\\')
}

pub(crate) fn looks_like_url(source: &str) -> bool {
    source.contains("://")
}

pub(crate) async fn fetch_registry_index(registry_url: &str) -> Result<PluginRegistryIndex> {
    let response = reqwest::get(registry_url)
        .await
        .with_context(|| format!("fetching plugin registry {registry_url}"))?;
    let status = response.status();
    if !status.is_success() {
        if status == reqwest::StatusCode::NOT_FOUND && registry_url == DEFAULT_REGISTRY_URL {
            bail!(
                "the public plugin registry is not populated yet; use --registry <url> to point at a custom registry"
            );
        }
        bail!("plugin registry returned HTTP {status} for {registry_url}");
    }
    response
        .json::<PluginRegistryIndex>()
        .await
        .context("parsing plugin registry JSON")
}

pub(crate) async fn download_registry_plugin(
    registry_url: &str,
    source: &str,
    cache_data_dir: Option<&Path>,
) -> Result<DownloadedPlugin> {
    let index = fetch_registry_index(registry_url).await?;
    if let Some(data_dir) = cache_data_dir {
        write_cached_registry_index(data_dir, registry_url, &index)?;
    }
    let spec = parse_plugin_spec(source)?;
    let entry = resolve_entry(&index, &spec)?.clone();

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Check the status code in the message: 404 means the URL/path is wrong, 401/403 means credentials are needed, 429/5xx means back off and retry
  2. Verify the registry URL resolves and serves the index (curl -I <url>)
  3. Add a secret/token mechanism if the registry requires auth — do not bake tokens into the URL in shared configs
  4. Retry with backoff for 429/5xx; the index fetch is idempotent

Example fix

# before
zeroclaw plugin install p --registry https://reg.example/plugins   # 404

# after
curl -I https://reg.example/registry/index.json   # confirm 200
zeroclaw plugin install p --registry https://reg.example/registry/index.json
Defensive patterns

Strategy: retry

Validate before calling

// Cheap reachability + status probe of the registry before running installs:
let s = reqwest::get(&registry_url).await?.status();
if !s.is_success() { /* skip/defer install, alert on 5xx, fix URL on 404 */ }

Try / catch

// Retry with exponential backoff ONLY for 429/5xx; treat 401/403 as config
// errors (credentials) and 404 as a wrong URL. Parse the numeric status from
// the 'plugin registry returned HTTP {status}' message.

Prevention

When it happens

Trigger: GET on the registry index URL returning 401/403 (auth required), 404 for a custom registry (wrong path), 429 (rate limit), 5xx (registry down), or a redirect chain ending in an error. Triggered by download_registry_plugin via fetch_registry_index.

Common situations: Typo'd or outdated --registry URL (404); private registry needing a token (401/403); rate-limited CI hitting the registry repeatedly (429); registry outage or maintenance (502/503).

Related errors


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