{"record":{"id":"7d05a3d093f2d25e","repo":"iOfficeAI/OfficeCLI","slug":"corrupt-batch","errorCode":"corrupt_batch","errorMessage":"Plugin '{resolved.Manifest.Name}' emitted a JSON array; protocol v1 requires JSONL (one BatchItem per line).","messagePattern":"Plugin '(.+?)' emitted a JSON array; protocol v1 requires JSONL \\(one BatchItem per line\\)\\.","errorType":"exception","errorClass":"CliException","httpStatus":null,"severity":"error","filePath":"src/officecli/CommandBuilder.Plugins.cs","lineNumber":284,"sourceCode":"                };\n\n            // The plugin's declared target format determines which schema\n            // tree to validate emitted props against (default: docx).\n            var schemaFormat = resolved.Manifest.ResolveTargetFormat();\n\n            // Run the plugin and stream JSONL.\n            var items = new List<BatchItem>();\n            var findings = new List<LintFinding>();\n\n            void OnLine(string raw)\n            {\n                // Mirrors DumpReaderInvoker: strip per-line UTF-8 BOM so a\n                // plugin that BOMs every JSONL line passes lint as well as\n                // it passes invocation.\n                var line = raw.TrimStart('﻿').Trim();\n                if (line.Length == 0) return;\n                if (line[0] == '[')\n                    throw new CliException(\n                        $\"Plugin '{resolved.Manifest.Name}' emitted a JSON array; protocol v1 requires JSONL (one BatchItem per line).\")\n                    { Code = \"corrupt_batch\" };\n\n                BatchItem? item;\n                try { item = JsonSerializer.Deserialize(line, BatchJsonContext.Default.BatchItem); }\n                catch (JsonException ex)\n                {\n                    throw new CliException(\n                        $\"Plugin '{resolved.Manifest.Name}' emitted invalid JSON at line #{items.Count}: {ex.Message}\")\n                    { Code = \"plugin_contract_violation\" };\n                }\n                if (item is null) return;\n                items.Add(item);\n            }\n\n            var idle = resolved.Manifest.ResolveIdleTimeout(\"dump\");\n            var runResult = PluginProcess.Run(new PluginProcess.RunOptions\n            {","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/CommandBuilder.Plugins.cs#L266-L302","documentation":"The plugin protocol v1 requires JSONL on stdout — exactly one BatchItem JSON object per line. This fires when the first non-whitespace character of a stdout line is '[', meaning the plugin emitted a whole JSON array. A JSON array cannot be streamed line-by-line, so it is treated as a corrupt batch.","triggerScenarios":"A dump-reader plugin, when invoked with 'dump <fixture>', prints '[{...},{...}]' (a JSON array) instead of '{...}\\n{...}\\n' (JSONL).","commonSituations":"Plugin author used a JSON serializer's array mode; ported a library that emits arrays; the plugin prints a debug header array before items.","solutions":["Emit one JSON object per line (JSONL), not a wrapping array.","In the plugin, serialize each BatchItem and WriteLine it immediately (streaming).","Remove any leading '[' debug output."],"exampleFix":"// before (plugin stdout): [{\"type\":\"add\",...},{\"type\":\"add\",...}]\n// after  (plugin stdout): {\"type\":\"add\",...}\n{\"type\":\"add\",...}","handlingStrategy":"validation","validationCode":"// Plugin author: assert stdout is JSONL before publishing.\nforeach (var item in items)\n    Console.WriteLine(JsonSerializer.Serialize(item)); // object per line, no array wrapper","typeGuard":"static bool IsJsonLNotArray(string firstLine) =>\n    firstLine.TrimStart().Length > 0 && firstLine.TrimStart()[0] != '[';","tryCatchPattern":"// Lint runner: catch corrupt_batch and report as a plugin fix.\ntry { LintPlugin(plugin, fixture); }\ncatch (CliException e) when (e.Code == \"corrupt_batch\") {\n    ReportPluginDefect($\"{plugin} must emit JSONL, not a JSON array.\");\n}","preventionTips":["Protocol v1 = JSONL (one object/line), never a top-level array.","Stream items with WriteLine as they are produced.","Unit-test plugin stdout: every non-empty line must parse as a standalone object."],"tags":["plugins","lint","jsonl","plugin-contract","protocol-v1"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}