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

grok_cli does not accept api_key; use `grok login`, or expor

Error message

grok_cli does not accept api_key; use `grok login`, or export `XAI_API_KEY` and list it in the alias env_passthrough

What it means

The grok_cli family wraps the local grok CLI over ACP and authenticates through the CLI's own stored login (grok login), never through an api_key. The factory therefore refuses to construct the provider when a non-empty api_key is present (has_api_key true), and the message names the two supported routes.

Source

Thrown at crates/zeroclaw-providers/src/factory.rs:1530

    }

    fn fallback_auth_ready(&self, _key: Option<&str>, _opts: &ModelProviderRuntimeOptions) -> bool {
        true
    }
}

impl FamilyProviderFactory for GrokCliModelProviderConfig {
    const ENDPOINT: ProviderEndpoint = ProviderEndpoint::CliBacked;

    fn create_provider(
        &self,
        alias: &str,
        key: Option<&str>,
        _api_url: Option<&str>,
        opts: &ModelProviderRuntimeOptions,
    ) -> Result<Box<dyn ModelProvider>> {
        if has_api_key(key) {
            anyhow::bail!(
                "grok_cli does not accept api_key; use `grok login`, or export `XAI_API_KEY` and list it in the alias env_passthrough"
            );
        }
        Ok(Box::new(
            crate::grok_cli::GrokCliModelProvider::builder(alias)
                .binary_path(self.binary_path.as_deref())
                .working_directory(&self.working_directory)
                .env_passthrough(self.env_passthrough.clone())
                .extra_args(self.extra_args.clone())
                .max_acp_stdout_bytes(self.max_acp_stdout_bytes)
                .timeout_secs(self.base.timeout_secs)
                // Optional send-path only: alias `vision = true` makes
                // ZeroClaw emit ACP image blocks. Grok still advertises
                // image=false through 0.2.118 and does not reliably use the
                // pixels; leave unset in production until upstream vision works.
                .vision_enabled(opts.vision == Some(true))
                .build()?,
        ))

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Remove api_key from the grok_cli alias config and environment so the CLI's own login is used
  2. Run `grok login` once so the CLI has stored credentials
  3. If you want key-based xAI access, use the xai family provider with api_key instead of grok_cli
  4. To hand XAI_API_KEY to the CLI process, list it in the alias env_passthrough, not api_key

Example fix

# before
[model_provider.grok]
family = "grok_cli"
api_key = "xai-..."

# after
[model_provider.grok]
family = "grok_cli"
# authenticate via `grok login`; optional:
# env_passthrough = ["XAI_API_KEY"]
Defensive patterns

Strategy: validation

Validate before calling

// Strip the key before factory construction for CLI-backed families
let key = if family == "grok_cli" { None } else { key };
let provider = factory.create_provider(alias, key, api_url, &opts)?;

Try / catch

match factory.create_provider(alias, key, api_url, &opts) {
    Ok(p) => Ok(p),
    Err(e) if e.to_string().contains("grok_cli does not accept api_key") => {
        // config bug: remove api_key, or switch family to "xai" for key auth
        Err(e)
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Creating a grok_cli alias while api_key is set from config or resolved from environment - for example copying an xai (HTTP) alias config to grok_cli and leaving the key in place, or an exported XAI_API_KEY being picked up as the alias key.

Common situations: Migrating alias configs between xai and grok_cli families; XAI_API_KEY exported globally and auto-resolved as the provider key; templates that set api_key on every alias.

Related errors


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