Consistently catch LSP panics - #1405
Conversation
|
I want to make sure I understand what's going on here. The old code caught unconditionally, right? A panic with no recovering caller was caught on the R thread, ferried back, and re-raised on the calling thread? The hook then saw no boundary and aborted. The new code aborts at the original panic site instead. Both paths abort, so is there a case where the difference is visible? I ask because the condition removes the guard that the old comment called out: "a Rust unwind crossing those C frames is UB". Can we keep |
|
@juliasilge Assuming this is about I'd say the new behaviour of checking if there is a recovery boundary is marginally better because of the immediate process exit in case of panic. I also like that this unifies Are you concerned about other kinds of |
|
Worth noting that in parallel to that commit for catching salsa cancellations we've started catching LSP panics. And since the legacy LSP is the main consumer of r_task (now that comms run directly on the R threads) it should be unexpected to go through that immediately aborting path, since we are now consistently installing recovery boundaries. Also since resume_unwind doesn't reinvoke the panic hook, re throwing panics unconditionally would result in a greater risk of a thread silently dying. Although we've been heading in the opposite direction of keeping the process alive in case of panics, we should arguably only recover in controlled situations rather than let the process in a partially working state. |
This PR revisits how LSP panics are caught to:
Catch panics produced in notification handlers and other parts of the LSP service. Previously only request handlers were properly guarded. Addresses A panic outside the LSP request path disables the R language server with no message positron#15666
Catch panics on the I/O and analysis task pools without aborting the process. Addresses A panicking analysis task aborts the whole R session instead of being caught positron#15667
Special care is taken for:
r_task(). This is mainly defensive, butr_task()is used in legacy parts of the LSP so we better be safe.Unlike panics in request handlers, panics in notification handlers are unrecoverable. See these screencasts, the first causes a notification panic and shuts down the LSP in an orderly fashion after notifying the user:
Screen.Recording.2026-09-11.at.16.24.33.mov
The second is a request panic that only notifies the user and keeps the LSP in working order:
Screen.Recording.2026-09-11.at.16.25.36.mov
To avoid spamming the user with notifications, panics are only reported once per handler. They keep being logged in the background so user reports are complete.
When a panic occurs in a background task (e.g. diagnostics), a notification is sent once. Further panics are logged only.
Screen.Recording.2026-09-11.at.16.51.18.mov
Positron Release Notes
New Features
Bug Fixes