You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Plugin help output truncated due to SyncStdout race in CleanupAllClients #1563
When running stripe apps --help, the CLI sometimes returns only the first line of help output ("Commands for building Stripe apps\n") instead of the full Cobra help text. The exit code is 0 and stderr is empty.
Root cause
In pkg/cmd/plugin_cmds.go, the help handler calls runPluginCmd, which does:
CleanupAllClients() calls hcplugin.CleanupClients(), which kills the plugin process. The plugin's stdout is piped to the host via go-plugin's SyncStdout goroutine (ClientConfig.SyncStdout = os.Stdout). After the gRPC RunCommand returns, the plugin has written all its help text to its stdout pipe buffer — but the SyncStdout goroutine in the host process hasn't finished copying that data to os.Stdout. When the CLI's main goroutine exits, Go terminates the SyncStdout goroutine mid-drain.
The truncation is reproducible when the plugin binary is cold (first invocation, not in OS page cache). A second invocation on the same machine reliably returns the full output.
Impact
Any stripe <plugin> --help command that goes through the go-plugin code path can be affected. Plugins with more subcommands (longer help text) are more likely to exhibit truncation because more data needs to drain through the pipe.
Steps to reproduce
Clear the OS page cache for the apps plugin binary (or use a fresh CI runner)
Run stripe apps --help
Observe only the first line of output
Suggested fix
Wait for the SyncStdout pipe to be fully drained before exiting. Options:
In plugin.Run(): after RunCommand returns, wait for the plugin process to fully exit (which closes the pipe and lets SyncStdout drain) before returning.
In runPluginCmd(): add a small delay or explicit wait between plugin.Run() and CleanupAllClients() to give the SyncStdout goroutine time to finish.
In the go-plugin ClientConfig: set Managed: false and manually manage the client lifecycle, calling client.Kill() only after confirming the stdout pipe has been drained.
Workaround
Retry the command if the output appears truncated (exit code 0 but missing expected content). The second invocation succeeds because the binary is now in the OS page cache.
Activity