iOfficeAI/OfficeCLI · error · CliException

corrupt_batch

corrupt_batch

Error message

Plugin '{resolved.Manifest.Name}' emitted a JSON array; protocol v1 requires JSONL (one BatchItem per line).

What it means

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.

Source

Thrown at src/officecli/CommandBuilder.Plugins.cs:284

                };

            // The plugin's declared target format determines which schema
            // tree to validate emitted props against (default: docx).
            var schemaFormat = resolved.Manifest.ResolveTargetFormat();

            // Run the plugin and stream JSONL.
            var items = new List<BatchItem>();
            var findings = new List<LintFinding>();

            void OnLine(string raw)
            {
                // Mirrors DumpReaderInvoker: strip per-line UTF-8 BOM so a
                // plugin that BOMs every JSONL line passes lint as well as
                // it passes invocation.
                var line = raw.TrimStart('').Trim();
                if (line.Length == 0) return;
                if (line[0] == '[')
                    throw new CliException(
                        $"Plugin '{resolved.Manifest.Name}' emitted a JSON array; protocol v1 requires JSONL (one BatchItem per line).")
                    { Code = "corrupt_batch" };

                BatchItem? item;
                try { item = JsonSerializer.Deserialize(line, BatchJsonContext.Default.BatchItem); }
                catch (JsonException ex)
                {
                    throw new CliException(
                        $"Plugin '{resolved.Manifest.Name}' emitted invalid JSON at line #{items.Count}: {ex.Message}")
                    { Code = "plugin_contract_violation" };
                }
                if (item is null) return;
                items.Add(item);
            }

            var idle = resolved.Manifest.ResolveIdleTimeout("dump");
            var runResult = PluginProcess.Run(new PluginProcess.RunOptions
            {

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Emit one JSON object per line (JSONL), not a wrapping array.
  2. In the plugin, serialize each BatchItem and WriteLine it immediately (streaming).
  3. Remove any leading '[' debug output.

Example fix

// before (plugin stdout): [{"type":"add",...},{"type":"add",...}]
// after  (plugin stdout): {"type":"add",...}
{"type":"add",...}
Defensive patterns

Strategy: validation

Validate before calling

// Plugin author: assert stdout is JSONL before publishing.
foreach (var item in items)
    Console.WriteLine(JsonSerializer.Serialize(item)); // object per line, no array wrapper

Type guard

static bool IsJsonLNotArray(string firstLine) =>
    firstLine.TrimStart().Length > 0 && firstLine.TrimStart()[0] != '[';

Try / catch

// Lint runner: catch corrupt_batch and report as a plugin fix.
try { LintPlugin(plugin, fixture); }
catch (CliException e) when (e.Code == "corrupt_batch") {
    ReportPluginDefect($"{plugin} must emit JSONL, not a JSON array.");
}

Prevention

When it happens

Trigger: A dump-reader plugin, when invoked with 'dump <fixture>', prints '[{...},{...}]' (a JSON array) instead of '{...}\n{...}\n' (JSONL).

Common situations: Plugin author used a JSON serializer's array mode; ported a library that emits arrays; the plugin prints a debug header array before items.

Related errors


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