xai-org/grok-build · error

--agents: invalid JSON: {e}

Error message

--agents: invalid JSON: {e}

What it means

parse_cli_agents parses the --agents CLI argument as a JSON object mapping agent names to their definitions. If the whole string is not valid JSON, the serde error is wrapped as "--agents: invalid JSON: {e}". The library throws it so the failure is attributed to the --agents flag rather than some later agent-resolution step.

Source

Thrown at crates/codegen/xai-grok-pager/src/headless/cli.rs:217

pub(crate) enum ResolvedAgent {
    FilePath(PathBuf),
    Name(String),
}

pub(crate) fn resolve_agent_arg(agent: &str) -> ResolvedAgent {
    let path = std::path::Path::new(agent);
    if path.exists() && path.is_file() {
        ResolvedAgent::FilePath(dunce::canonicalize(path).unwrap_or_else(|_| path.to_path_buf()))
    } else {
        ResolvedAgent::Name(agent.to_string())
    }
}

pub(crate) fn parse_cli_agents(
    json: &str,
) -> anyhow::Result<Vec<xai_grok_shell::agent::config::AgentDefinition>> {
    let map: std::collections::HashMap<String, serde_json::Value> =
        serde_json::from_str(json).map_err(|e| anyhow::anyhow!("--agents: invalid JSON: {e}"))?;
    let mut agents = Vec::with_capacity(map.len());
    for (name, mut value) in map {
        if let serde_json::Value::Object(ref mut obj) = value {
            if !obj.contains_key("promptBody")
                && let Some(prompt) = obj.remove("prompt")
            {
                obj.insert("promptBody".to_string(), prompt);
            }
            obj.entry("name".to_string())
                .or_insert_with(|| serde_json::Value::String(name.clone()));
            obj.entry("description".to_string())
                .or_insert_with(|| serde_json::Value::String(name.clone()));
        }
        let mut def = xai_grok_shell::agent::config::AgentDefinition::from_json(&value)
            .map_err(|e| anyhow::anyhow!("--agents: failed to parse '{name}': {e}"))?;
        def.name = name;
        agents.push(def);
    }

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Validate the argument with jq . (or echo '<value>' | jq) to see the serde-reported syntax error.
  2. Ensure the value is a JSON object {"name": {...}, ...}, not an array or path.
  3. Serialize programmatically (jq -n, JSON.stringify, serde_json::json!) and pass via a shell-safe quoted variable or @file if supported.
  4. Single-quote the whole value on POSIX shells to prevent quote stripping.

Example fix

// before
--agents {"reviewer":{"prompt":"review"}}   // unquoted, shell eats braces
// after
--agents '{"reviewer":{"prompt":"review"}}'
Defensive patterns

Strategy: validation

Validate before calling

fn agents_arg_is_object_map(s: &str) -> bool {
    serde_json::from_str::<std::collections::HashMap<String, serde_json::Value>>(s).is_ok()
}
// guard before invoking run_single_turn with the --agents value

Try / catch

match parse_cli_agents(&agents_arg) {
    Ok(agents) => agents,
    Err(e) if e.to_string().starts_with("--agents: invalid JSON") => {
        eprintln!("--agents must be a JSON object map: {e}");
        std::process::exit(2);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Passing --agents a value that serde_json::from_str rejects: not JSON at all, an array instead of an object, a truncated/partial map, or shell-mangled quoting (single quotes removed, inner double quotes unescaped).

Common situations: Shell quoting problems on Linux/macOS vs Windows; building the flag value by string concatenation in a script; passing a YAML/TOML agent file's contents instead of JSON; passing a file path instead of inline JSON.

Understand the failure class

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/2dc2671a568f6fa1. Report an issue: GitHub.