{"record":{"id":"5f05a49a818fd2f5","repo":"iOfficeAI/OfficeCLI","slug":"each-calculatedfields-entry-must-be-a-json-object","errorCode":null,"errorMessage":"each calculatedFields entry must be a JSON object","messagePattern":"each calculatedFields entry must be a JSON object","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/PivotTableHelper.Definition.cs","lineNumber":1719,"sourceCode":"    /// </summary>\n    private static List<(string name, string formula)> ParseCalculatedFieldSpecs(\n        Dictionary<string, string> properties)\n    {\n        var result = new List<(string, string)>();\n\n        // JSON form first — higher fidelity when user wants multiple specs.\n        if (properties.TryGetValue(\"calculatedFields\", out var jsonRaw)\n            && !string.IsNullOrWhiteSpace(jsonRaw))\n        {\n            try\n            {\n                using var doc = System.Text.Json.JsonDocument.Parse(jsonRaw);\n                if (doc.RootElement.ValueKind != System.Text.Json.JsonValueKind.Array)\n                    throw new ArgumentException(\"'calculatedFields' must be a JSON array\");\n                foreach (var el in doc.RootElement.EnumerateArray())\n                {\n                    if (el.ValueKind != System.Text.Json.JsonValueKind.Object)\n                        throw new ArgumentException(\"each calculatedFields entry must be a JSON object\");\n                    string? name = null, formula = null;\n                    foreach (var p in el.EnumerateObject())\n                    {\n                        if (p.NameEquals(\"name\")) name = p.Value.GetString();\n                        else if (p.NameEquals(\"formula\")) formula = p.Value.GetString();\n                    }\n                    if (name != null && formula != null)\n                        result.Add((name, formula));\n                }\n            }\n            catch (System.Text.Json.JsonException ex)\n            {\n                throw new ArgumentException($\"invalid JSON for calculatedFields: {ex.Message}\");\n            }\n        }\n\n        // Numbered + bare calculatedField props (ordinal sort so calculatedField1\n        // appears before calculatedField2 regardless of insertion order).","sourceCodeStart":1701,"sourceCodeEnd":1737,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/PivotTableHelper.Definition.cs#L1701-L1737","documentation":"After confirming the JSON root is an array, the parser iterates each element and requires it to be a JSON object (so it can extract 'name' and 'formula' properties). Any non-object element — string, number, array, null, boolean — is rejected immediately. This mirrors the strict-enum policy used elsewhere: surface malformed input at Add/Set time instead of producing a malformed pivot.","triggerScenarios":"calculatedFields=[\"X:=A1\"] (array of strings rather than objects); calculatedFields=[42]; calculatedFields=[null]; mixed arrays like [{...},\"Y:=B1\"].","commonSituations":"User assumes the array accepts the same colon-string syntax as the singular prop; migration from a different tool that used string specs; auto-generated JSON that mixed element shapes.","solutions":["Make every array element an object: calculatedFields=[{\"name\":\"X\",\"formula\":\"=A1\"}]","If you prefer the colon-string form, use the singular prop: calculatedField=X:=A1 (or calculatedField1=..., calculatedField2=...)","Inspect each element of your generated array before submission"],"exampleFix":"// before\ncalculatedFields=\"[\\\"X:=A1\\\", \\\"Y:=B1\\\"]\"\n// after\ncalculatedFields=\"[{\\\"name\\\":\\\"X\\\",\\\"formula\\\":\\\"=A1\\\"},{\\\"name\\\":\\\"Y\\\",\\\"formula\\\":\\\"=B1\\\"}]\"","handlingStrategy":"validation","validationCode":"foreach (var el in doc.RootElement.EnumerateArray())\n    if (el.ValueKind != JsonValueKind.Object)\n        throw new InvalidOperationException(\"Each calculatedFields entry must be a JSON object\");","typeGuard":"static bool AllEntriesAreObjects(string json)\n{\n    try\n    {\n        var root = JsonDocument.Parse(json).RootElement;\n        return root.ValueKind == JsonValueKind.Array &&\n               root.EnumerateArray().All(e => e.ValueKind == JsonValueKind.Object);\n    }\n    catch { return false; }\n}","tryCatchPattern":"try { AddCalculatedFields(props); }\ncatch (ArgumentException ex) when (ex.Message.Contains(\"must be a JSON object\"))\n{ /* convert string entries to {name,formula} objects */ }","preventionTips":["Do not mix string and object elements in the array","Generate the array from typed records via the serializer","Prefer the colon-form prop when you have a single string spec"],"tags":["pivottable","calculated-field","json","argument-validation"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}