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

proxy.scope='services' requires a non-empty proxy.services l

Error message

proxy.scope='services' requires a non-empty proxy.services list when proxy is enabled

What it means

With proxy.enabled = true and proxy.scope = "services", ProxyConfig::validate requires a non-empty normalized proxy.services list. A services-scoped proxy that selects no services would route nothing, which almost certainly indicates an unfinished config; normalization means a list containing only blank strings counts as empty too.

Source

Thrown at crates/zeroclaw-config/src/schema.rs:9878

        for selector in self.normalized_services() {
            if !is_supported_proxy_service_selector(&selector) {
                anyhow::bail!(
                    "Unsupported proxy service selector '{selector}'. Use tool `proxy_config` action `list_services` for valid values"
                );
            }
        }

        if self.enabled && !self.has_any_proxy_url() {
            anyhow::bail!(
                "Proxy is enabled but no proxy URL is configured. Set at least one of http_proxy, https_proxy, or all_proxy"
            );
        }

        if self.enabled
            && self.scope == ProxyScope::Services
            && self.normalized_services().is_empty()
        {
            anyhow::bail!(
                "proxy.scope='services' requires a non-empty proxy.services list when proxy is enabled"
            );
        }

        Ok(())
    }

    pub fn should_apply_to_service(&self, service_key: &str) -> bool {
        if !self.enabled {
            return false;
        }

        match self.scope {
            ProxyScope::Environment => false,
            ProxyScope::Zeroclaw => true,
            ProxyScope::Services => {
                let service_key = service_key.trim().to_ascii_lowercase();
                if service_key.is_empty() {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Populate proxy.services with valid selectors, e.g. ["model_provider.*", "tool.browser"].
  2. If you meant to proxy everything, change proxy.scope back to the broader value instead of listing services.
  3. Filter blank entries from programmatically-built lists before writing the config.

Example fix

# before
[proxy]
enabled = true
scope = "services"

# after
[proxy]
enabled = true
scope = "services"
services = ["model_provider.*", "tool.*"]
Defensive patterns

Strategy: validation

Validate before calling

// scope=services + enabled requires a non-empty normalized list
if cfg.proxy.enabled
    && cfg.proxy.scope == ProxyScope::Services
    && cfg.proxy.normalized_services().is_empty()
{
    // populate services or switch scope back before validate()
}

Type guard

fn services_scope_satisfied(enabled: bool, is_services_scope: bool, services: &[String]) -> bool {
    !enabled || !is_services_scope || services.iter().any(|s| !s.trim().is_empty())
}

Try / catch

match cfg.proxy.validate() {
    Err(e) if e.to_string().contains("requires a non-empty proxy.services list") => {
        // add selectors like "model_provider.*" or change scope away from "services"
    }
    other => other,
}

Prevention

When it happens

Trigger: proxy.enabled = true, proxy.scope = "services", and proxy.services missing, set to [], or containing only blank entries.

Common situations: Switching scope from all to services without populating the list; services lists built from env expansion that came through empty; trimming entries down to nothing during cleanup.

Related errors


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