iOfficeAI/OfficeCLI · error · ArgumentException

Batch input must be a JSON array. Got: {rootKind.ToString().

Error message

Batch input must be a JSON array. Got: {rootKind.ToString().ToLowerInvariant()}. Wrap a single item like [{"command":"get","path":"/"}].

What it means

Thrown when the batch JSON root (after any envelope auto-unwrap) is neither an array nor null. The pre-validation at line 282-294 converts what used to be a generic JsonException exposing the C# type name into a stable, model-agnostic ArgumentException naming the actual root kind and how to fix it (wrap a single item in an array).

Source

Thrown at src/officecli/CommandBuilder.Batch.cs:291

                && jsonDoc.RootElement.TryGetProperty("data", out var envData)
                && envData.ValueKind == System.Text.Json.JsonValueKind.Array)
            {
                jsonText = envData.GetRawText();
                jsonDoc.Dispose();
                jsonDoc = System.Text.Json.JsonDocument.Parse(jsonText);
            }
            using var _jsonDocOwner = jsonDoc;
            var rootKind = jsonDoc.RootElement.ValueKind;
            if (rootKind != System.Text.Json.JsonValueKind.Array
                && rootKind != System.Text.Json.JsonValueKind.Null)
            {
                // BUG-R7-10: when the batch input is a JSON object/string/etc.
                // (not an array), Deserialize<List<BatchItem>> threw a generic
                // JsonException whose message exposed the C# generic type name
                // (`System.Collections.Generic.List`1[OfficeCli.BatchItem]`).
                // Convert it to a human-friendly error first so AI agents and
                // humans see a stable, model-agnostic diagnostic.
                throw new ArgumentException(
                    $"Batch input must be a JSON array. Got: {rootKind.ToString().ToLowerInvariant()}. "
                    + "Wrap a single item like [{\"command\":\"get\",\"path\":\"/\"}].");
            }
            if (jsonDoc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Array)
            {
                int ri = 0;
                foreach (var elem in jsonDoc.RootElement.EnumerateArray())
                {
                    if (elem.ValueKind == System.Text.Json.JsonValueKind.Object)
                    {
                        var unknown = new List<string>();
                        foreach (var prop in elem.EnumerateObject())
                        {
                            if (!BatchItem.KnownFields.Contains(prop.Name))
                                unknown.Add(prop.Name);
                        }
                        if (unknown.Count > 0)
                            throw new ArgumentException($"batch item[{ri}]: unknown field(s) {string.Join(", ", unknown.Select(f => $"\"{f}\""))}. Valid fields: {string.Join(", ", BatchItem.KnownFields)}");

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Wrap the item(s) in a JSON array: `[{"command":"get","path":"/"}]`.
  2. If piping from `dump --json`, the envelope auto-unwrap handles `{"data":[...]}`; ensure data is an array.
  3. Validate the root kind before sending: `jq type ops.json` should report `array`.

Example fix

// before
{"command":"get","path":"/"}

// after
[{"command":"get","path":"/"}]
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check the batch root kind.
using var d = System.Text.Json.JsonDocument.Parse(jsonText);
if (d.RootElement.ValueKind != System.Text.Json.JsonValueKind.Array
    && d.RootElement.ValueKind != System.Text.Json.JsonValueKind.Null)
    throw new ArgumentException("batch input must be a JSON array; wrap single items in []");

Type guard

// Guard: root is array (after optional envelope unwrap).
static bool IsBatchArrayRoot(string json)
{
    try
    {
        using var d = System.Text.Json.JsonDocument.Parse(json);
        var k = d.RootElement.ValueKind;
        if (k == System.Text.Json.JsonValueKind.Object
            && d.RootElement.TryGetProperty("data", out var data)
            && data.ValueKind == System.Text.Json.JsonValueKind.Array) return true;
        return k == System.Text.Json.JsonValueKind.Array || k == System.Text.Json.JsonValueKind.Null;
    }
    catch { return false; }
}

Prevention

When it happens

Trigger: `officecli batch file.docx --commands '{"command":"get"}'` (bare object), or `--commands '"hello"'` (string), or `--commands '42'` (number). After envelope unwrap the root kind is object/string/number, not array/null.

Common situations: An agent emits a single command as an object instead of a one-element array; a hand-written JSON uses an object root; the input was a dump envelope whose `data` was not an array; copy-paste dropped the enclosing `[ ]`.

Related errors


AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13). Data as JSON: /api/errors/f4a7393d1fa6f108. Report an issue: GitHub.