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

grok_cli env_passthrough entry `{name}` is provider-owned (`

Error message

grok_cli env_passthrough entry `{name}` is provider-owned (`XAI_*` other than `XAI_API_KEY`, and all `GROK_*`); other names (for example tool credentials) may be listed explicitly, and Grok CLI policy flags belong in `extra_args`

What it means

env_passthrough blocks provider-owned names: every GROK_* variable and all XAI_* except XAI_API_KEY, because ZeroClaw injects those itself and a passthrough entry would fight the provider. Other names (e.g. tool credentials) are explicitly allowed; Grok CLI policy flags belong in extra_args, not env.

Source

Thrown at crates/zeroclaw-providers/src/grok_cli.rs:526

                "grok_cli max_acp_stdout_bytes must be between {} and {} bytes",
                acp::MIN_ACP_STDOUT_LIMIT_BYTES,
                acp::MAX_ACP_STDOUT_LIMIT_BYTES
            );
        }
        Ok(limit)
    }

    fn normalize_and_validate_env_passthrough(names: Vec<String>) -> anyhow::Result<Vec<String>> {
        let mut normalized: Vec<String> = Vec::with_capacity(names.len());
        for name in names {
            let name = name.trim();
            if !is_valid_env_var_name(name) {
                anyhow::bail!(
                    "grok_cli env_passthrough entry `{name}` is invalid; expected [A-Za-z_][A-Za-z0-9_]*"
                );
            }
            if is_disallowed_provider_env_var(name) {
                anyhow::bail!(
                    "grok_cli env_passthrough entry `{name}` is provider-owned \
                     (`XAI_*` other than `XAI_API_KEY`, and all `GROK_*`); \
                     other names (for example tool credentials) may be listed \
                     explicitly, and Grok CLI policy flags belong in `extra_args`"
                );
            }
            if !normalized
                .iter()
                .any(|existing| env_names_equal(existing.as_str(), name))
            {
                // Preserve the operator-supplied spelling; equality is
                // case-insensitive on Windows when checking membership later.
                normalized.push(name.to_string());
            }
        }
        Ok(normalized)
    }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Remove the provider-owned entry; configure the equivalent behavior through provider fields or extra_args
  2. Keep XAI_API_KEY in env_passthrough only if the key must come from the environment
  3. For tool credentials (non-provider names), leave them listed - they are allowed

Example fix

# before
env_passthrough = ["XAI_API_KEY", "GROK_MODEL", "PATH"]

# after
env_passthrough = ["XAI_API_KEY", "PATH"]  # model goes in the model field / extra_args
Defensive patterns

Strategy: validation

Validate before calling

fn is_provider_owned_env(name: &str) -> bool {
    let n = name.to_ascii_uppercase();
    n.starts_with("GROK_") || (n.starts_with("XAI_") && n != "XAI_API_KEY")
}

fn passthrough_entry_allowed(name: &str) -> bool {
    valid_env_var_name(name) && !is_provider_owned_env(name)
}

Type guard

fn is_provider_owned_env(name: &str) -> bool {
    let n = name.to_ascii_uppercase();
    n.starts_with("GROK_") || (n.starts_with("XAI_") && n != "XAI_API_KEY")
}

Prevention

When it happens

Trigger: Listing XAI_API_TIMEOUT, XAI_BASE_URL, or GROK_MODEL in env_passthrough; migrating a manual grok CLI invocation that exported GROK_* vars and keeping them in the passthrough list.

Common situations: Ported shell wrappers; attempts to override model or policy through environment instead of config; security reviews tightening env hygiene.

Related errors


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