plugin: don't panic on BrokenPipe when lightningd has died#146
Merged
vincenzopalazzo merged 1 commit intoMay 6, 2026
Merged
Conversation
Two paths in the plugin runtime previously called `.unwrap()` on writes to stdout, which panic with BrokenPipe if lightningd has exited or closed the pipe ahead of the plugin: - `Plugin::log()` writes a JSON-RPC log notification on every log line - `Plugin::start()` writes the response to each handled RPC/hook call When lightningd dies (e.g. cleanly during shutdown, or unexpectedly via a fatal signal), all of its plugins should also exit cleanly — but a panic in either of these paths leaves a half-dead Rust plugin that has already lost its parent. In practice this surfaced as orphan plugin processes in the host's process table after lightningd crashed, spinning on the now-empty stdin until something else cleaned them up. The EOF check on `read_line` already added in 4f8df9a handles the "parent closed our stdin" case, but it only kicks in *after* we successfully wrote a response. If the parent dies between us reading its request and writing back, the unwrap fires before we ever loop. Make both writers best-effort: on `log()` swallow the error (logs are inherently lossy and we don't want a stray log call to take down the plugin), and in the RPC response path break out of the read loop so the plugin terminates cleanly rather than panicking. The subsequent `read_line` would have returned `Ok(0)` anyway, so the behavior on a healthy parent is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Plugin::log()and the response-writing path inPlugin::start()both call.unwrap()on writes to stdout. Whenlightningdexits or closes the pipe between the plugin reading a request and writing back, the unwrap panics withBrokenPipeinstead of letting the plugin exit cleanly.log()we swallow the error (logs are lossy by design), and instart()we break out of the read loop so the plugin terminates cleanly. On a healthy parent the behavior is unchanged — the subsequentread_linewould have returnedOk(0)and broken the loop anyway.Background
Observed in the wild while running folgore under CLN v25.09.3 on a low-RAM host. When
lightningddied for any reason (fatal signal, OOM kill), one or morefolgore_pluginRust processes were left as orphans, spinning on stdin in an empty read loop or — worse — having panicked on a half-written response and exited mid-state.The EOF check on
read_linealready added in 4f8df9a covers the "parent closed our stdin" case, but only after a clean iteration. If the parent dies between request and response, theunwrapfires first.After applying both fixes locally and verifying via
[patch.crates-io]against the actualfolgore_pluginbinary across multiplelightningddeath cycles: zero orphan plugin processes, clean exits every time.Test plan
cargo build --release -p clightningrpc-plugincargo test)lightningdwith SIGKILL, confirm the plugin exits within one stdin read cycle rather than panicking or orphaning🤖 Generated with Claude Code