{"record":{"id":"a8acd6c9921f3e21","repo":"iOfficeAI/OfficeCLI","slug":"unexpected-end-of-json-for-batchitem","errorCode":null,"errorMessage":"Unexpected end of JSON for BatchItem","messagePattern":"Unexpected end of JSON for BatchItem","errorType":"validation","errorClass":"JsonException","httpStatus":null,"severity":"error","filePath":"src/officecli/BatchTypes.cs","lineNumber":112,"sourceCode":"                case \"index\": item.Index = reader.TokenType == JsonTokenType.Null ? null : reader.GetInt32(); break;\n                case \"after\": item.After = reader.GetString(); break;\n                case \"before\": item.Before = reader.GetString(); break;\n                case \"to\": item.To = reader.GetString(); break;\n                case \"path2\": item.Path2 = reader.GetString(); break;\n                case \"props\": item.Props = PropsConverter.Read(ref reader, typeof(Dictionary<string, string>), options); break;\n                case \"selector\": item.Selector = reader.GetString(); break;\n                case \"text\": item.Text = reader.GetString(); break;\n                case \"mode\": item.Mode = reader.GetString(); break;\n                case \"depth\": item.Depth = reader.TokenType == JsonTokenType.Null ? null : reader.GetInt32(); break;\n                case \"part\": item.Part = reader.GetString(); break;\n                case \"xpath\": item.Xpath = reader.GetString(); break;\n                case \"action\": item.Action = reader.GetString(); break;\n                case \"xml\": item.Xml = reader.GetString(); break;\n                case \"dumpversion\": item.DumpVersion = reader.TokenType == JsonTokenType.Null ? null : reader.GetInt32(); break;\n                default: reader.Skip(); break;\n            }\n        }\n        throw new JsonException(\"Unexpected end of JSON for BatchItem\");\n    }\n\n    public override void Write(Utf8JsonWriter writer, BatchItem value, JsonSerializerOptions options)\n    {\n        writer.WriteStartObject();\n        if (!string.IsNullOrEmpty(value.Command)) writer.WriteString(\"command\", value.Command);\n        if (value.Path != null) writer.WriteString(\"path\", value.Path);\n        if (value.Parent != null) writer.WriteString(\"parent\", value.Parent);\n        if (value.Type != null) writer.WriteString(\"type\", value.Type);\n        if (value.From != null) writer.WriteString(\"from\", value.From);\n        if (value.Index.HasValue) writer.WriteNumber(\"index\", value.Index.Value);\n        if (value.After != null) writer.WriteString(\"after\", value.After);\n        if (value.Before != null) writer.WriteString(\"before\", value.Before);\n        if (value.To != null) writer.WriteString(\"to\", value.To);\n        if (value.Path2 != null) writer.WriteString(\"path2\", value.Path2);\n        if (value.Props != null) { writer.WritePropertyName(\"props\"); PropsConverter.Write(writer, value.Props, options); }\n        if (value.Selector != null) writer.WriteString(\"selector\", value.Selector);\n        if (value.Text != null) writer.WriteString(\"text\", value.Text);","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/BatchTypes.cs#L94-L130","documentation":"Thrown by the custom JsonConverter<BatchItem>.Read in BatchTypes.cs when the JSON reader reaches end-of-input before finding the closing `}` of a batch item object. The reader loop (`while (reader.Read())`) returns false on truncation, falls through the property switch, and hits the guard at line 112. It means the batch JSON stream was cut off mid-object — a malformed or incompletely-written input.","triggerScenarios":"`officecli batch foo.docx --input ops.json` where ops.json is truncated like `[{\"command\":\"get\",\"path\":\"/\"` (no closing brace). Also reachable when stdin is a broken pipe (`cat ops.json | head -c 100 | officecli batch foo.docx`), when a file write was interrupted, or when an AI agent streams a partial JSON object and the connection drops before completion.","commonSituations":"A batch script writes the JSON file and crashes mid-write; a network copy of the ops file is incomplete; an LLM agent emits a JSON array but truncates the last object; piping through a tool that mangles/buffers and cuts the stream; editing the JSON by hand and forgetting the final `}`.","solutions":["Inspect the input source end-to-end: `tail -c 50 ops.json` to confirm it ends with `]` and the last object closes with `}`.","Validate the file is complete JSON before invoking batch: `jq empty ops.json` (jq exits non-zero on truncation).","If reading from stdin, verify the producer writes the full payload and closes the pipe; redirect to a file first and check its size/ending.","Regenerate the batch ops file from `dump` or your script so it is well-formed."],"exampleFix":"// before (truncated)\n[{\"command\":\"get\",\"path\":\"/\n\n// after (complete)\n[{\"command\":\"get\",\"path\":\"/\"}]","handlingStrategy":"validation","validationCode":"// Validate the batch JSON is complete and well-formed before invoking batch.\nusing var doc = System.Text.Json.JsonDocument.Parse(jsonText);\nif (doc.RootElement.ValueKind != System.Text.Json.JsonValueKind.Array)\n    throw new InvalidOperationException(\"batch input must be a JSON array\");\nforeach (var elem in doc.RootElement.EnumerateArray())\n{\n    if (elem.ValueKind != System.Text.Json.JsonValueKind.Object\n        && elem.ValueKind != System.Text.Json.JsonValueKind.Null)\n        throw new InvalidOperationException(\"batch array entries must be objects\");\n}\n// Shell equivalent before calling officecli:\n//   jq empty ops.json && officecli batch foo.docx --input ops.json","typeGuard":"// C# guard that a JSON string is a complete array of objects/nulls.\nstatic bool IsCompleteBatchArray(string json)\n{\n    try\n    {\n        using var d = System.Text.Json.JsonDocument.Parse(json);\n        if (d.RootElement.ValueKind != System.Text.Json.JsonValueKind.Array) return false;\n        foreach (var e in d.RootElement.EnumerateArray())\n            if (e.ValueKind != System.Text.Json.JsonValueKind.Object\n                && e.ValueKind != System.Text.Json.JsonValueKind.Null) return false;\n        return true;\n    }\n    catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Write batch ops files atomically (write temp, then rename) so a crash never leaves a truncated file.","Pipe through `jq empty <file>` as a pre-check in shell wrappers before invoking officecli batch.","If an agent streams JSON, buffer the full payload and validate completeness before passing to batch.","End stdin with a clear EOF and check the producer's exit code, not just the pipe close."],"tags":["json","batch","deserialization","truncated-input"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}