Flow-Launcher/Flow.Launcher · error · InvalidDataException

<JSON-RPC plugin process stderr>

Error message

<JSON-RPC plugin process stderr>

What it means

Thrown as InvalidDataException carrying the raw UTF-8 stderr of the child JSON-RPC plugin process. The v1 JsonRPCPlugin captures the plugin executable's stdout and stderr into separate buffers via CopyToAsync, and when the process exits with a non-empty errorBuffer (case (_, not 0)), the buffered stderr text is re-thrown. This is the primary channel through which a misbehaving/buggy plugin process surfaces its crash message to Flow Launcher's host.

Source

Thrown at Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs:220

                // token expire won't instantly trigger the exception, 
                // manually kill process at before
                await process.WaitForExitAsync(token);
                await Task.WhenAll(sourceCopyTask, errorCopyTask);
            }
            catch (OperationCanceledException)
            {
                await sourceBuffer.DisposeAsync();
                return Stream.Null;
            }

            switch (sourceBuffer.Length, errorBuffer.Length)
            {
                case (0, 0):
                    const string errorMessage = "Empty JSON-RPC Response.";
                    Context.API.LogWarn(ClassName, errorMessage);
                    break;
                case (_, not 0):
                    throw new InvalidDataException(Encoding.UTF8.GetString(errorBuffer.ToArray())); // The process has exited with an error message
            }

            sourceBuffer.Seek(0, SeekOrigin.Begin);

            return sourceBuffer;
        }

        public override async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
        {
            var request = new JsonRPCRequestModel(RequestId++,
                "query",
                new object[]
                {
                    query.Search
                },
                Settings?.Inner);

            var output = await RequestAsync(request, token);

View on GitHub (pinned to 7fc63b07bb)

Solutions

  1. Read the message text — it IS the plugin's stderr output, so it names the actual failing module/line in the plugin.
  2. Verify the plugin's declared executable and runtime exist (e.g. python.exe on PATH, node version) by running the plugin's command manually.
  3. Check the plugin's WorkingDirectory and language interpreter config in its plugin.json.
  4. If the plugin intentionally logs non-fatal diagnostics to stderr, this v1 host cannot distinguish them from fatal errors — switch the plugin to the V2 protocol or have it write logs to a file instead.
  5. Update or reinstall the offending plugin from the plugin manifest.

Example fix

// before (plugin v1 — any stderr is fatal)
import sys
print('loading model...', file=sys.stderr)  // triggers the throw

// after — keep stderr clean, log to a file
import logging
logging.basicConfig(filename='plugin.log')
logging.info('loading model...')
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the plugin executable and runtime before invoking
var psi = new ProcessStartInfo(pluginExecutable) { RedirectStandardError = true };
if (!File.Exists(psi.FileName)) return; // guard before RequestAsync

Type guard

null

Try / catch

try { var results = await plugin.QueryAsync(query, token); }
catch (InvalidDataException ex) when (ex.Message.Contains("JSON-RPC"))
{
    Context.API.LogException(ClassName, $"Plugin {plugin.Name} stderr: {ex.Message}", ex);
    return new List<Result>(); // graceful empty results
}

Prevention

When it happens

Trigger: The plugin child process writes anything to stderr before or while exiting (an uncaught exception, a Python traceback, a Node stack trace, a failed assertion). Specifically: sourceBuffer.Length is anything AND errorBuffer.Length is not 0 after process.WaitForExitAsync completes. This includes the case where the plugin wrote both valid stdout and an error to stderr — error wins.

Common situations: Plugin written in Python/Node/Go crashes on startup (missing dependency, wrong runtime version); plugin throws an unhandled exception during query handling that gets dumped to stderr; plugin executable path points to a binary whose required runtime is not installed; plugin logs a warning to stderr then continues (this would still throw even though the plugin is otherwise healthy, since v1 treats any stderr as fatal).

Related errors


AI-assisted analysis of Flow-Launcher/Flow.Launcher@7fc63b07bb (2026-08-13). Data as JSON: /api/errors/70c5eefdd054ea2a. Report an issue: GitHub.