{"record":{"id":"9c830b1ecdc9ce61","repo":"iOfficeAI/OfficeCLI","slug":"plugin-stream-closed","errorCode":"plugin_stream_closed","errorMessage":"Format-handler session for '{_plugin.Manifest.Name}' is no longer usable (stream was closed earlier).","messagePattern":"Format-handler session for '(.+?)' is no longer usable \\(stream was closed earlier\\)\\.","errorType":"exception","errorClass":"CliException","httpStatus":null,"severity":"error","filePath":"src/officecli/Core/Plugins/FormatHandlerSession.cs","lineNumber":155,"sourceCode":"                ? null\n                : JsonSerializer.Deserialize(reply.ToJsonString(), PluginJsonContext.Default.PluginSessionCapabilities);\n        }\n        catch (JsonException)\n        {\n            _sessionCaps = null;\n        }\n    }\n\n    /// <summary>\n    /// Send a request envelope and synchronously wait for the matching reply.\n    /// Throws <see cref=\"CliException\"/> on protocol error, IO failure, or\n    /// plugin-reported error responses.\n    /// </summary>\n    public JsonNode? Send(string msgType, string? command, JsonObject? args = null, JsonObject? props = null)\n    {\n        if (_disposed) throw new ObjectDisposedException(nameof(FormatHandlerSession));\n        if (_broken)\n            throw new CliException(\n                $\"Format-handler session for '{_plugin.Manifest.Name}' is no longer usable (stream was closed earlier).\")\n            { Code = \"plugin_stream_closed\" };\n\n        // Capability gate: short-circuit verbs the plugin already declared it\n        // does not support, avoiding a wasted round-trip and ambiguous errors.\n        if (command is not null && _sessionCaps?.Capabilities?.Commands is { Count: > 0 } cmds\n            && !cmds.Contains(command))\n        {\n            throw new CliException(\n                $\"Format-handler plugin '{_plugin.Manifest.Name}' does not implement command '{command}'.\")\n            { Code = \"unsupported_command\" };\n        }\n\n        var verbForTimeout = command ?? msgType;\n        var idle = _plugin.Manifest.ResolveIdleTimeout(verbForTimeout);\n        return SendRaw(msgType, command, args, props, idle);\n    }\n","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/iOfficeAI/OfficeCLI/blob/1ced45e900782c5083ed550ddf328ee974e425e7/src/officecli/Core/Plugins/FormatHandlerSession.cs#L137-L173","documentation":"CliException (code 'plugin_stream_closed') thrown by Send when the _broken flag is set. _broken is latched at six sites (FormatHandlerSession.cs:212,234,243,261,268,297) after an earlier IO/protocol failure on the session's stdin/stdout. Once broken, the session refuses further sends rather than retry into a half-dead stream.","triggerScenarios":"Any Send call after a prior request hit an IO error, malformed reply, or stream closure that set _broken=true. ObjectDisposedException is checked first (for explicit disposal).","commonSituations":"Plugin crashed and the session tried to keep going; a previous command timed out / broke the wire format and subsequent commands reuse the same proxy; long-lived resident session whose plugin was restarted underneath.","solutions":["Recreate the format-handler session (restart the plugin) instead of reusing the broken one.","Investigate the original failure that set _broken — the first error in the log is the root cause, not this one.","For resident hosts, add session-health checks and recycle the proxy on break."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"if (session.IsBroken) { session = new FormatHandlerSession(plugin); /* recycle */ }","typeGuard":"static bool IsSessionUsable(FormatHandlerSession s) => !s.IsBroken && !s.IsDisposed;","tryCatchPattern":"try { session.Send(...); }\ncatch (CliException ex) when (ex.Code == \"plugin_stream_closed\")\n{ session = new FormatHandlerSession(plugin); /* recreate and retry once */ }","preventionTips":["Investigate the first error in the log — _broken is a downstream symptom.","Recycle the proxy/session after any IO error rather than reusing it.","Add liveness checks for resident sessions whose plugins may restart."],"tags":["plugins","format-handler","session","io","cli-exception"],"backgroundTag":null,"analyzedSha":"1ced45e900782c5083ed550ddf328ee974e425e7","analyzedAt":"2026-08-13T13:01:07.193Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}