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
- Make every array element an object: runs="[{\"text\":\"a\"},{\"text\":\"b\"}]".
- If you have plain strings, wrap each as {"text": "<string>"} before serializing.
- 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
- Define a run record type and serialize a list of records so elements are always objects.
- Validate each element's kind before invoking Add.
- Avoid hand-building JSON strings for run arrays.
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
- 'runs' must be a JSON array of run objects.
- Invalid JSON for 'runs': {jex.Message}
- Parent path must be /SheetName/CellRef for adding a run
- Anchor sheet '{aSegs[0]}' must match target sheet '{colSheet
- Invalid 'outline' value: '{addColOutline}'. Expected an inte
AI-assisted analysis of iOfficeAI/OfficeCLI@1ced45e900 (2026-08-13).
Data as JSON: /api/errors/3b8190ea7934adf8.
Report an issue: GitHub.