iOfficeAI/OfficeCLI · error · System.ArgumentException

Each run in 'runs' must be a JSON object.

Error message

Each run in 'runs' must be a JSON object.

What it means

Thrown by ApplyRichTextToCell while enumerating the runs JSON array: one of the array's elements is not a JSON object. Each run must be an object carrying a text field and optional style properties. A string, number, or nested array element triggers this guard during EnumerateArray.

Source

Thrown at src/officecli/Handlers/Excel/ExcelHandler.Add.Cells.cs:1444

        {
            sst = new SharedStringTable();
            sstPart.SharedStringTable = sst;
        }

        var ssi = new SharedStringItem();

        var gatheredRuns = new List<(string text, Dictionary<string, string> props)>();
        if (properties.TryGetValue("runs", out var runsJson) && !string.IsNullOrWhiteSpace(runsJson))
        {
            try
            {
                using var jdoc = System.Text.Json.JsonDocument.Parse(runsJson);
                if (jdoc.RootElement.ValueKind != System.Text.Json.JsonValueKind.Array)
                    throw new ArgumentException("'runs' must be a JSON array of run objects.");
                foreach (var el in jdoc.RootElement.EnumerateArray())
                {
                    if (el.ValueKind != System.Text.Json.JsonValueKind.Object)
                        throw new ArgumentException("Each run in 'runs' must be a JSON object.");
                    string text = "";
                    var pd = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                    foreach (var p in el.EnumerateObject())
                    {
                        var sv = p.Value.ValueKind switch
                        {
                            System.Text.Json.JsonValueKind.True => "true",
                            System.Text.Json.JsonValueKind.False => "false",
                            System.Text.Json.JsonValueKind.Null => "",
                            System.Text.Json.JsonValueKind.Number => p.Value.GetRawText(),
                            _ => p.Value.GetString() ?? ""
                        };
                        if (p.NameEquals("text")) text = sv;
                        else pd[p.Name] = sv;
                    }
                    OfficeCli.Core.ParseHelpers.ValidateXmlText(text, "richtext run text");
                    gatheredRuns.Add((text, pd));
                }

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Make every array element an object: runs="[{\"text\":\"a\"},{\"text\":\"b\"}]".
  2. If you have plain strings, wrap each as {"text": "<string>"} before serializing.
  3. Switch to the legacy run1/run2 numbered syntax for ad-hoc multi-run input.

Example fix

// before
props["runs"] = "[\"Hello\",\"World\"]";

// after
props["runs"] = "[{\"text\":\"Hello\"},{\"text\":\"World\"}]";
Defensive patterns

Strategy: validation

Validate before calling

if (props.TryGetValue("runs", out var runsJson) && !string.IsNullOrWhiteSpace(runsJson))
{
    using var jdoc = System.Text.Json.JsonDocument.Parse(runsJson);
    foreach (var el in jdoc.RootElement.EnumerateArray())
        if (el.ValueKind != System.Text.Json.JsonValueKind.Object)
            throw new InvalidOperationException("each run must be a JSON object");
}
handler.Add(parentPath, "richtext", pos, props);

Type guard

static bool RunsJsonAllObjects(string? json)
{
    if (string.IsNullOrWhiteSpace(json)) return true;
    using var d = System.Text.Json.JsonDocument.Parse(json);
    if (d.RootElement.ValueKind != System.Text.Json.JsonValueKind.Array) return false;
    foreach (var el in d.RootElement.EnumerateArray())
        if (el.ValueKind != System.Text.Json.JsonValueKind.Object) return false;
    return true;
}

Prevention

When it happens

Trigger: properties["runs"]="[\"Hello\",42]" or runs="[[{...}]]". Any array element whose ValueKind is not Object. The check runs per element, so the first non-object element throws.

Common situations: User passes an array of plain strings instead of objects. JSON built by joining text values without wrapping each in {}. A serializer that flattens run specs.

Related errors


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