xai-org/grok-build · error

paths.{key} is not an array

Error message

paths.{key} is not an array

What it means

merge_paths adds a PATHS key (e.g. env PATH-style path lists) to a TOML settings table. It inserts an empty array if the key is missing, then requires the existing value to be a TOML array; if the key already exists but holds a non-array value (string, table, etc.) it cannot be merged, so this error is thrown.

Source

Thrown at crates/codegen/xai-grok-shell/src/claude_import.rs:1016

/// are appended. Returns the number of newly added entries.
fn merge_paths(
    table: &mut TomlMap<String, TomlValue>,
    key: &str,
    new_paths: &[&str],
) -> anyhow::Result<usize> {
    let paths = table
        .entry("paths")
        .or_insert_with(|| TomlValue::Table(TomlMap::new()));
    let paths_table = paths
        .as_table_mut()
        .ok_or_else(|| anyhow::anyhow!("[paths] is not a table"))?;

    let arr = paths_table
        .entry(key)
        .or_insert_with(|| TomlValue::Array(Vec::new()));
    let existing = arr
        .as_array_mut()
        .ok_or_else(|| anyhow::anyhow!("paths.{key} is not an array"))?;

    let existing_set: std::collections::HashSet<String> = existing
        .iter()
        .filter_map(|v| v.as_str().map(|s| s.to_string()))
        .collect();

    let mut count = 0usize;
    for p in new_paths {
        if !existing_set.contains(*p) {
            existing.push(TomlValue::String(p.to_string()));
            count += 1;
        }
    }
    Ok(count)
}

/// Merge `Hook` items into `<hooks_dir>/imported-from-claude.json`.
///

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Open the settings.toml and change paths.<key> to an array of strings, e.g. paths.cfg = ["/usr/bin"]
  2. Delete the offending paths.<key> line and re-run the import so it is recreated as an empty array
  3. Validate the TOML with a schema check before running the import

Example fix

# before
[paths]
cfg = "/usr/local/bin"
# after
[paths]
cfg = ["/usr/local/bin"]
Defensive patterns

Strategy: validation

Validate before calling

// before calling the import
let v = settings.get("paths").and_then(|p| p.get(key));
if let Some(v) = v {
    assert!(v.is_array(), "paths.{key} must be an array of strings");
}

Type guard

fn is_string_array(v: &toml::Value) -> bool {
    v.as_array().map_or(true, |a| a.iter().all(|x| x.is_str()))
}

Try / catch

match merge_paths(&mut cfg, key, entries) {
    Err(e) if e.to_string().contains("is not an array") => {
        // reset the key and retry once
        cfg["paths"][key] = toml::Value::Array(vec![]);
        merge_paths(&mut cfg, key, entries)
    }
    r => r,
}

Prevention

When it happens

Trigger: apply_items_to_config / merge_paths is called on a settings.toml in which paths.<key> exists but was hand-edited to a string or inline table instead of an array of strings.

Common situations: Users editing Claude-style settings.toml by hand (e.g. paths.cfg = "/usr/bin" instead of paths.cfg = ["/usr/bin"]), or a schema change from older versions that stored paths as a single string.

Related errors


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