iOfficeAI/OfficeCLI · warning · JsonException

Unexpected end of JSON

Error message

Unexpected end of JSON

What it means

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.

Source

Thrown at src/officecli/BatchTypes.cs:33

        // Array form: ["key=value", ...]. This mirrors the single-command MCP
        // `props` argument and the CLI `--prop key=value` flag, so an agent that
        // learned props from `set`/`add` produces the same shape inside a batch
        // item. Before this, batch props was object-only and every array-form
        // batch failed with "Expected object for props" — observed as a 100%
        // batch-failure for models that (correctly) reused the single-command
        // props shape. Lenient split on the first '=' matches McpServer.ParseProps.
        if (reader.TokenType == JsonTokenType.StartArray)
        {
            while (reader.Read())
            {
                if (reader.TokenType == JsonTokenType.EndArray) return dict;
                if (reader.TokenType != JsonTokenType.String)
                    throw new JsonException("Expected \"key=value\" string in props array");
                var kv = reader.GetString()!;
                var eq = kv.IndexOf('=');
                if (eq > 0) dict[kv[..eq]] = kv[(eq + 1)..];  // skip malformed, as ParseProps does
            }
            throw new JsonException("Unexpected end of JSON");
        }
        if (reader.TokenType != JsonTokenType.StartObject)
            throw new JsonException("Expected object or [\"key=value\"] array for props");
        while (reader.Read())
        {
            if (reader.TokenType == JsonTokenType.EndObject) return dict;
            if (reader.TokenType != JsonTokenType.PropertyName)
                throw new JsonException("Expected property name");
            var key = reader.GetString()!;
            reader.Read();
            var value = reader.TokenType switch
            {
                JsonTokenType.String => reader.GetString()!,
                JsonTokenType.Number => reader.TryGetInt64(out var l) ? l.ToString() : reader.GetDouble().ToString(),
                JsonTokenType.True => "true",
                JsonTokenType.False => "false",
                JsonTokenType.Null => "",
                _ => throw new JsonException($"Unexpected token {reader.TokenType} for prop value '{key}'")

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Validate the batch JSON parses fully (JSON.parse on the client) before sending.
  2. Ensure the payload is not truncated by a body-size limit on the resident/MCP transport.
  3. Build batch JSON with a real serializer rather than manual string concatenation.

Example fix

// before: batchJson string ends mid-array: ..."props":["a=1","b=2"
// after: produce complete, validated JSON
const items = [{ command:'set', path:'/A1', props:['a=1','b=2'] }];
const batchJson = JSON.stringify(items); // always well-formed
Defensive patterns

Strategy: validation

Validate before calling

// Validate the whole batch payload parses end-to-end before sending
const items = buildItems();
const batchJson = JSON.stringify(items);
JSON.parse(batchJson); // throws here if truncated/malformed — fail before the resident does
await doc.batch(items);

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


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