fix: say how the semantic provider died, not just that it stopped - #5
Merged
CesarManzoCode merged 1 commit intoAug 30, 2026
Merged
Conversation
On Fedora on 2026-08-30 stage 58 reported `analyzer_starts=1` and then `rust-analyzer did not answer: initialize: the server stopped`, and there was nothing else. `ausearch -m SECCOMP` over the exact seconds of the run showed no SECCOMP, no AVC, no SELinux denial and no other kill. So the machine held exactly one sentence about the death, and that sentence is rule 10's failure shape: it says the reading failed and nothing about what happened. A process killed by the seccomp filter, a process that could not find its toolchain, and a process that panicked all close a pipe, and from `request_once` all three were the same event. Two things the kernel already knew were being thrown away. **How the process ended.** `Analyzer` holds the `Child` and never asked it. `epitaph` waits half a second — the pipe closing and the process being reaped are two events in that order, and asking at the instant of the first answers "still running" for a process that has already died — and then says the exit status, or the signal and its name. `SIGSYS` is 31, and 31 on its own tells the person reading nothing; a process killed by the filter dies of exactly that signal, so if that is what happened on Fedora the next run will say so in the refusal itself. **What it wrote on the way out.** `launch::spawn` has always given a confined program a `stderr` pipe and nothing on this path ever read it — which is not only lost evidence but a latent hang, because a pipe whose reader never empties it blocks the writer on a full buffer, and a server stopped mid-indexing looks exactly like one that hung. It is drained continuously now into a 4 KiB tail: the *last* bytes, because a server that logs its way through an indexing pass would otherwise fill any buffer with progress before reaching the sentence that matters, and the diagnosis says how much was dropped. `OnTheHost` piped its `stderr` for the same reason, so a death is diagnosed the same way whichever spawner started the process; its old `Stdio::null()` was avoiding the undrained-pipe hang that no longer exists. Both death paths carry it, and that is a race rather than two cases: a server that dies before the request is written fails with `EPIPE` in `send`, one that dies just after fails as a disconnected channel in `request_once`, and which happens is timing. One of the two carrying the epitaph would have been a coin toss. Nothing else moves. No syscall was added, no policy, no grant, no confinement, no fallback, no verify stage — the audit trail says this was not a seccomp kill, and guessing at a cause before the machine has been asked is what this change exists to stop. Proven here with a stand-in that dies the way the property needs (rule 8): `SIGSYS` from a shell is not an approximation of a seccomp kill, it is the same signal. Five tests cover the signal, the status, the message surviving, silence being stated as silence, and a megabyte of log neither blocking nor being quoted. End to end through `thalyx session`, a `renombrar-simbolo` against a provider that dies now answers: rust-analyzer did not answer: `initialize`: the server stopped — the process was killed by signal 31 (SIGSYS); its stderr, 45 bytes: error: could not find the sysroot; giving up Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WtoE5f1628XjnDcoCWCUy3
CesarManzoCode
deleted the
claude/rust-analyzer-death-diagnostics-2krdy2
branch
September 5, 2026 03:01
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.
On Fedora on 2026-08-30 stage 58 reported
analyzer_starts=1and thenrust-analyzer did not answer: initialize: the server stopped, and there was nothing else.ausearch -m SECCOMPover the exact seconds of the run showed no SECCOMP, no AVC, no SELinux denial and no other kill. So the machine held exactly one sentence about the death, and that sentence is rule 10's failure shape: it says the reading failed and nothing about what happened. A process killed by the seccomp filter, a process that could not find its toolchain, and a process that panicked all close a pipe, and fromrequest_onceall three were the same event.Two things the kernel already knew were being thrown away.
How the process ended.
Analyzerholds theChildand never asked it.epitaphwaits half a second — the pipe closing and the process being reaped are two events in that order, and asking at the instant of the first answers "still running" for a process that has already died — and then says the exit status, or the signal and its name.SIGSYSis 31, and 31 on its own tells the person reading nothing; a process killed by the filter dies of exactly that signal, so if that is what happened on Fedora the next run will say so in the refusal itself.What it wrote on the way out.
launch::spawnhas always given a confined program astderrpipe and nothing on this path ever read it — which is not only lost evidence but a latent hang, because a pipe whose reader never empties it blocks the writer on a full buffer, and a server stopped mid-indexing looks exactly like one that hung. It is drained continuously now into a 4 KiB tail: the last bytes, because a server that logs its way through an indexing pass would otherwise fill any buffer with progress before reaching the sentence that matters, and the diagnosis says how much was dropped.OnTheHostpiped itsstderrfor the same reason, so a death is diagnosed the same way whichever spawner started the process; its oldStdio::null()was avoiding the undrained-pipe hang that no longer exists.Both death paths carry it, and that is a race rather than two cases: a server that dies before the request is written fails with
EPIPEinsend, one that dies just after fails as a disconnected channel inrequest_once, and which happens is timing. One of the two carrying the epitaph would have been a coin toss.Nothing else moves. No syscall was added, no policy, no grant, no confinement, no fallback, no verify stage — the audit trail says this was not a seccomp kill, and guessing at a cause before the machine has been asked is what this change exists to stop.
Proven here with a stand-in that dies the way the property needs (rule 8):
SIGSYSfrom a shell is not an approximation of a seccomp kill, it is the same signal. Five tests cover the signal, the status, the message surviving, silence being stated as silence, and a megabyte of log neither blocking nor being quoted. End to end throughthalyx session, arenombrar-simboloagainst a provider that dies now answers:rust-analyzer did not answer:
initialize: the server stopped — theprocess was killed by signal 31 (SIGSYS); its stderr, 45 bytes: error:
could not find the sysroot; giving up
Claude-Session: https://claude.ai/code/session_01WtoE5f1628XjnDcoCWCUy3