{"record":{"id":"983afca12a63c7e7","repo":"iOfficeAI/OfficeCLI","slug":"unexpected-end-of-json","errorCode":null,"errorMessage":"Unexpected end of JSON","messagePattern":"Unexpected end of JSON","errorType":"validation","errorClass":"JsonException","httpStatus":null,"severity":"warning","filePath":"src/officecli/BatchTypes.cs","lineNumber":33,"sourceCode":"        // Array form: [\"key=value\", ...]. This mirrors the single-command MCP\n        // `props` argument and the CLI `--prop key=value` flag, so an agent that\n        // learned props from `set`/`add` produces the same shape inside a batch\n        // item. Before this, batch props was object-only and every array-form\n        // batch failed with \"Expected object for props\" — observed as a 100%\n        // batch-failure for models that (correctly) reused the single-command\n        // props shape. Lenient split on the first '=' matches McpServer.ParseProps.\n        if (reader.TokenType == JsonTokenType.StartArray)\n        {\n            while (reader.Read())\n            {\n                if (reader.TokenType == JsonTokenType.EndArray) return dict;\n                if (reader.TokenType != JsonTokenType.String)\n                    throw new JsonException(\"Expected \\\"key=value\\\" string in props array\");\n                var kv = reader.GetString()!;\n                var eq = kv.IndexOf('=');\n                if (eq > 0) dict[kv[..eq]] = kv[(eq + 1)..];  // skip malformed, as ParseProps does\n            }\n            throw new JsonException(\"Unexpected end of JSON\");\n        }\n        if (reader.TokenType != JsonTokenType.StartObject)\n            throw new JsonException(\"Expected object or [\\\"key=value\\\"] array for props\");\n        while (reader.Read())\n        {\n            if (reader.TokenType == JsonTokenType.EndObject) return dict;\n            if (reader.TokenType != JsonTokenType.PropertyName)\n                throw new JsonException(\"Expected property name\");\n            var key = reader.GetString()!;\n            reader.Read();\n            var value = reader.TokenType switch\n            {\n                JsonTokenType.String => reader.GetString()!,\n                JsonTokenType.Number => reader.TryGetInt64(out var l) ? l.ToString() : reader.GetDouble().ToString(),\n                JsonTokenType.True => \"true\",\n                JsonTokenType.False => \"false\",\n                JsonTokenType.Null => \"\",\n                _ => throw new JsonException($\"Unexpected token {reader.TokenType} for prop value '{key}'\")","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/BatchTypes.cs#L15-L51","documentation":"JsonException from the ARRAY branch of LenientStringDictionaryConverter.Read: the JSON stream ended (reader returned false / ran out of bytes) before the closing ] of the props array was seen. This is a truncated/malformed payload, not a value-type problem.","triggerScenarios":"A batch JSON string cut off mid-array (network truncation, buffer limit, string concatenation bug); a streaming deserializer that stopped early; an unterminated props array.","commonSituations":"Sending a very large batch whose JSON was truncated by a size cap; building batchJson via string slicing that dropped the tail; copy/paste of a partial JSON example.","solutions":["Validate the batch JSON parses fully (JSON.parse on the client) before sending.","Ensure the payload is not truncated by a body-size limit on the resident/MCP transport.","Build batch JSON with a real serializer rather than manual string concatenation."],"exampleFix":"// before: batchJson string ends mid-array: ...\"props\":[\"a=1\",\"b=2\"\n// after: produce complete, validated JSON\nconst items = [{ command:'set', path:'/A1', props:['a=1','b=2'] }];\nconst batchJson = JSON.stringify(items); // always well-formed","handlingStrategy":"validation","validationCode":"// Validate the whole batch payload parses end-to-end before sending\nconst items = buildItems();\nconst batchJson = JSON.stringify(items);\nJSON.parse(batchJson); // throws here if truncated/malformed — fail before the resident does\nawait doc.batch(items);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build batch JSON with JSON.stringify, never hand-concatenated strings.","Check the resident/MCP transport max body size and chunk large batches.","Round-trip parse your payload as a smoke test in dev."],"tags":["batch","json","props","truncated","deserialization"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}