{"record":{"id":"f02a817e2829efc0","repo":"iOfficeAI/OfficeCLI","slug":"comment-runs-invalid-json-array-ex-message","errorCode":null,"errorMessage":"comment runs: invalid JSON array — {ex.Message}","messagePattern":"comment runs: invalid JSON array — (.+?)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Handlers/Excel/ExcelHandler.Helpers.Node.cs","lineNumber":979,"sourceCode":"            // Non-generic Add(JsonNode) — the generic Add<T> overload carries\n            // RequiresUnreferencedCode (IL2026) though it never serializes a JsonNode.\n            arr.Add((System.Text.Json.Nodes.JsonNode)o);\n        }\n        return arr.ToJsonString();\n    }\n\n    // Build a CommentText from a `runs=<json array>` value, one <r> per run\n    // carrying its own <rPr>. Run vocabulary mirrors rich-text cells\n    // (bold/italic/strike/underline/superscript/subscript/size/color/font) so\n    // the two paths share one input contract. Comment runs keep the Tahoma-9\n    // indexed-81 default for facets a run leaves unspecified.\n    internal static CommentText BuildCommentTextFromRuns(string runsJson)\n    {\n        System.Text.Json.Nodes.JsonArray? arr;\n        try { arr = System.Text.Json.Nodes.JsonNode.Parse(runsJson) as System.Text.Json.Nodes.JsonArray; }\n        catch (System.Text.Json.JsonException ex)\n        {\n            throw new ArgumentException($\"comment runs: invalid JSON array — {ex.Message}\");\n        }\n        if (arr == null)\n            throw new ArgumentException(\"comment runs: value must be a JSON array\");\n\n        var ct = new CommentText();\n        foreach (var item in arr)\n        {\n            if (item is not System.Text.Json.Nodes.JsonObject o) continue;\n            var text = o[\"text\"]?.GetValue<string>() ?? \"\";\n            OfficeCli.Core.ParseHelpers.ValidateXmlText(text, \"comment run text\");\n\n            var rPr = new RunProperties();\n            bool RunBool(string key) => o[key] is { } n\n                && (n.GetValueKind() == System.Text.Json.JsonValueKind.True\n                    || (n.GetValueKind() == System.Text.Json.JsonValueKind.String && IsTruthy(n.GetValue<string>())));\n\n            if (RunBool(\"bold\")) rPr.AppendChild(new Bold());\n            if (RunBool(\"italic\")) rPr.AppendChild(new Italic());","sourceCodeStart":961,"sourceCodeEnd":997,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Handlers/Excel/ExcelHandler.Helpers.Node.cs#L961-L997","documentation":"Thrown by BuildCommentTextFromRuns when JsonNode.Parse raises a JsonException on the `runs=<json>` value for a comment. The runs value must be a parseable JSON string; any malformed JSON (missing quotes, trailing comma, unescaped char) bubbles up wrapped as an ArgumentException with the parser's message.","triggerScenarios":"Calling the comment API with runs='[{\"text\":\"hi\"},]' (trailing comma), runs='{text:1}' (single quotes), or any non-JSON text. JsonNode.Parse throws JsonException, caught and rethrown as ArgumentException.","commonSituations":"Generating runs JSON by string concatenation instead of a serializer; passing a Python/JS dict literal instead of JSON; shell quoting that strips double quotes around keys.","solutions":["Validate the runs string parses as JSON before sending it (JsonNode.Parse or json.loads in the caller).","Build the runs array with a JSON serializer (System.Text.Json.JsonSerializer.Serialize or json.dumps), never string concatenation.","Check the parser message in the exception text — it pinpoints the byte offset of the syntax error."],"exampleFix":"// before\nstring runs = \"[{\\\"text\\\":\\\"hi\\\"},]\"; // trailing comma\nBuildCommentTextFromRuns(runs);\n// after\nvar runsObj = new[] { new { text = \"hi\" } };\nstring runs = System.Text.Json.JsonSerializer.Serialize(runsObj);\nBuildCommentTextFromRuns(runs);","handlingStrategy":"validation","validationCode":"// Validate before the API call\nstatic bool IsValidRunsJson(string runsJson)\n{\n    try { using var doc = System.Text.Json.JsonDocument.Parse(runsJson); return doc.RootElement.ValueKind == System.Text.Json.JsonValueKind.Array; }\n    catch { return false; }\n}","typeGuard":"null","tryCatchPattern":"try\n{\n    BuildCommentTextFromRuns(runs);\n}\ncatch (ArgumentException ex) when (ex.Message.StartsWith(\"comment runs: invalid JSON\"))\n{\n    // surface parser offset to the user; do not retry with the same string\n}","preventionTips":["Always serialize runs with a JSON serializer, never hand-build the string.","Round-trip test any runs string through Parse before persisting.","Treat parser messages as authoritative for locating the bad byte."],"tags":["json","comments","excel","input-validation"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}