iOfficeAI/OfficeCLI · error · CliException

plugin_error

plugin_error

Error message

Format-handler plugin '{_plugin.Manifest.Name}' reported error on {command ?? msgType}: {msg}

What it means

Thrown when the plugin sends a well-formed error envelope: {"msg_type":"error","error":{"code":"...","message":"..."}}. This is the normal error-reporting channel — the plugin intentionally declined or failed the operation. The CliException's Code is set from the plugin's error.code field (defaulting to plugin_error), and the message field carries the plugin's human-readable explanation.

Source

Thrown at src/officecli/Core/Plugins/FormatHandlerSession.cs:257

                }

                if (reply is null)
                {
                    _broken = true;
                    throw new CliException(
                        $"Format-handler plugin '{_plugin.Manifest.Name}' reply is not a JSON object. First chars: \"{Truncate(line, 80)}\".")
                    { Code = "protocol_mismatch" };
                }

                var replyType = reply["msg_type"]?.GetValue<string>() ?? "";
                if (replyType == "ok")
                    return reply["result"];
                if (replyType == "error")
                {
                    var err = reply["error"]?.AsObject();
                    var code = err?["code"]?.GetValue<string>() ?? "plugin_error";
                    var msg = err?["message"]?.GetValue<string>() ?? "(no message)";
                    throw new CliException(
                        $"Format-handler plugin '{_plugin.Manifest.Name}' reported error on {command ?? msgType}: {msg}")
                    { Code = code };
                }
                _broken = true;
                throw new CliException(
                    $"Format-handler plugin '{_plugin.Manifest.Name}' replied with unknown msg_type '{replyType}'.")
                { Code = "protocol_mismatch" };
            }
            catch (IOException ex)
            {
                _broken = true;
                throw new CliException(
                    $"Format-handler plugin '{_plugin.Manifest.Name}' stdin/stdout I/O failed: {ex.Message}", ex)
                { Code = "plugin_stream_closed" };
            }
        }
    }

View on GitHub (pinned to 1ced45e900)

Solutions

  1. Read the CliException Message — it contains the plugin's explanation after the colon.
  2. Read the CliException Code — it carries the plugin's error.code, which may be more specific than plugin_error (e.g. file_corrupt, missing_dependency).
  3. Fix the root cause the plugin describes: repair the input file, install the dependency, correct the argument.
  4. Unlike protocol_mismatch errors, the session is still usable — you can send the next command without restarting.

Example fix

// before: caller treats all plugin errors as fatal
try { session.Send("save", null, args, props); }
catch (CliException) { session.Dispose(); throw; }  // overkill for plugin_error

// after: only restart on broken-session codes
try { session.Send("save", null, args, props); }
catch (CliException ex) {
    if (session.IsBroken) { session.Dispose(); throw; }
    // plugin_error is recoverable — log and continue or surface to user
    Console.Error.WriteLine($"Plugin reported: {ex.Message} [{ex.Code}]");
}
Defensive patterns

Strategy: try-catch

Try / catch

try
{
    var result = session.Send(msgType, command, args, props);
}
catch (CliException ex) when (ex.Code != null && ex.Code != "plugin_error" && !session.IsBroken)
{
    // Only protocol-level failures poison the session.
    // A plugin_error means the plugin intentionally declined — session is still usable.
    throw;
}
// For plugin_error specifically:
catch (CliException ex) when (ex.Code == "plugin_error" || !session.IsBroken)
{
    // Recoverable: log the message, surface to user, or fall back.
    Console.Error.WriteLine($"Plugin declined: {ex.Message} [{ex.Code}]");
}

Prevention

When it happens

Trigger: FormatHandlerSession.SendRaw reads a reply where reply["msg_type"] == "error". The code is extracted from reply["error"]["code"] (or "plugin_error" if absent); the message from reply["error"]["message"] (or "(no message)"). The session is NOT marked _broken — this is a recoverable per-command error, not a protocol failure.

Common situations: Plugin can't open a corrupted file and reports an error. Plugin encounters an unsupported feature in the target format. Plugin's external dependency (LibreOffice, a native library) is missing. Plugin rejects an invalid command argument. The error.code propagates as the CliException Code, so downstream error handling can branch on it.

Related errors


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