{"record":{"id":"6a354f2597155518","repo":"iOfficeAI/OfficeCLI","slug":"plugin-contract-violation-6a354f","errorCode":"plugin_contract_violation","errorMessage":"Dump-reader plugin '{plugin.Manifest.Name}' emitted invalid JSON at item #{itemIndex}: {ex.Message}","messagePattern":"Dump-reader plugin '(.+?)' emitted invalid JSON at item #(.+?): (.+?)","errorType":"exception","errorClass":"CliException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/Plugins/DumpReaderInvoker.cs","lineNumber":156,"sourceCode":"                };\n\n            // v6.4: now that the plugin has exited and all JSONL is buffered,\n            // open the handler on this thread and replay synchronously. See\n            // the rationale comment at the bufferedLines declaration above\n            // (OpenXml SDK package state not thread-safe under heavy\n            // multi-part Update-mode mutation).\n            using (var handler = DocumentHandlerFactory.Open(tmpOut, editable: true))\n            {\n                foreach (var line in bufferedLines)\n                {\n                    BatchItem? item;\n                    try\n                    {\n                        item = JsonSerializer.Deserialize(line, BatchJsonContext.Default.BatchItem);\n                    }\n                    catch (JsonException ex)\n                    {\n                        throw new CliException(\n                            $\"Dump-reader plugin '{plugin.Manifest.Name}' emitted invalid JSON at item #{itemIndex}: {ex.Message}\")\n                        { Code = \"plugin_contract_violation\" };\n                    }\n                    if (item is null)\n                        throw new CliException(\n                            $\"Dump-reader plugin '{plugin.Manifest.Name}' emitted null at item #{itemIndex}.\")\n                        { Code = \"plugin_contract_violation\" };\n\n                    try\n                    {\n                        CommandBuilder.ExecuteBatchItem(handler, item, json: false);\n                    }\n                    catch (Exception ex)\n                    {\n                        throw new CliException(\n                            $\"Dump-reader plugin '{plugin.Manifest.Name}' command #{itemIndex} ({item.Command}) failed while replaying: {ex.Message}\", ex)\n                        { Code = \"plugin_command_failed\" };\n                    }","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/Plugins/DumpReaderInvoker.cs#L138-L174","documentation":"CliException (code 'plugin_contract_violation') thrown when JsonSerializer.Deserialize<BatchItem> throws a JsonException on a buffered line, surfaced with the offending item index (0-based). Parse is deferred from the line callback to this main-thread replay loop for consistent item-index semantics.","triggerScenarios":"A JSONL line that is not valid JSON, or valid JSON that does not match the BatchItem schema (wrong types, missing required members). Trailing partial line from a crashed plugin is a common cause.","commonSituations":"Plugin truncated mid-line due to a crash; plugin emits a BatchItem with an unexpected property type; encoding mismatch producing stray bytes after BOM trim.","solutions":["Capture the plugin's raw stdout and inspect the line at the reported item index.","Validate each emitted line parses as a BatchItem against BatchJsonContext before shipping the plugin.","Ensure the plugin flushes complete lines and never writes partial JSON on crash."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Plugin-side: validate each line before printing.\nvar parsed = JsonSerializer.Deserialize<BatchItem>(line, BatchJsonContext.Default.BatchItem);\nif (parsed is null) throw new InvalidOperationException(\"Bad line\");","typeGuard":null,"tryCatchPattern":"try { DumpReaderInvoker.Run(source, ext); }\ncatch (CliException ex) when (ex.Code == \"plugin_contract_violation\" && ex.Message.Contains(\"invalid JSON\"))\n{ /* capture plugin output, report item index from message */ }","preventionTips":["Plugin CI should round-trip every emitted line through BatchJsonContext.","Never write partial JSON; flush only complete lines.","Log the offending line index when surfacing to users."],"tags":["plugins","dump-reader","json","protocol","cli-exception"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}