{"record":{"id":"9b5a1023e1812cda","repo":"linera-io/linera-protocol","slug":"child-process-self-already-exited-with-status","errorCode":null,"errorMessage":"Child process {self:?} already exited with status: {status}","messagePattern":"Child process (.+?) already exited with status: (.+?)","errorType":"exception","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"linera-service/src/util.rs","lineNumber":35,"sourceCode":"use linera_base::data_types::TimeDelta;\npub use linera_client::util::*;\nuse tracing::debug;\n\n/// Default pause, in seconds, inserted after `linera service` commands in readme e2e tests.\npub static DEFAULT_PAUSE_AFTER_LINERA_SERVICE_SECS: &str = \"3\";\n/// Default pause, in seconds, inserted after GraphQL mutations in readme e2e tests.\npub static DEFAULT_PAUSE_AFTER_GQL_MUTATIONS_SECS: &str = \"3\";\n\n/// Extension trait for [`tokio::process::Child`].\npub trait ChildExt: std::fmt::Debug {\n    /// Ensures the child process is still running, returning an error if it has exited.\n    fn ensure_is_running(&mut self) -> Result<()>;\n}\n\nimpl ChildExt for tokio::process::Child {\n    fn ensure_is_running(&mut self) -> Result<()> {\n        if let Some(status) = self.try_wait().context(\"try_wait child process\")? {\n            bail!(\"Child process {self:?} already exited with status: {status}\");\n        }\n        debug!(\"Child process {self:?} is running as expected.\");\n        Ok(())\n    }\n}\n\n/// Reads and deserializes a JSON value from the file at the given path.\npub fn read_json<T: serde::de::DeserializeOwned>(path: impl Into<std::path::PathBuf>) -> Result<T> {\n    Ok(serde_json::from_reader(fs_err::File::open(path)?)?)\n}\n\n/// Expands to the name of the current test function.\n#[cfg(with_testing)]\n#[macro_export]\nmacro_rules! test_name {\n    () => {\n        stdext::function_name!()\n            .strip_suffix(\"::{{closure}}\")","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-service/src/util.rs#L17-L53","documentation":"Raised by `ChildExt::ensure_is_running` (linera-service util.rs) when the tokio child process wrapped by the test harness (e.g. a linera-service node, faucet, or proxy started for a test) has already terminated — `try_wait()` returns `Some(status)`. It is the harness's crash detector: any later operation on the dead child reports this with the child's debug representation and exit status. The real failure happened earlier (the child's own crash), so this error is a symptom, not the root cause.","triggerScenarios":"Invoking any harness operation (query_node, process spawn, faucet call, etc.) that calls `ensure_is_running` after the wrapped child has exited — e.g. the node binary panicked at startup, exited due to bad arguments, hit a port conflict, or was killed by the OS/OOM.","commonSituations":"Parallel e2e tests racing for the same ports so one node exits immediately; a faucet/node failing on a missing genesis file; CI runners killing memory-heavy linera-service children; tests continuing after a child crash instead of aborting at the first failure.","solutions":["Look at the earlier output/stderr of the child process in the test logs — the actual crash reason is printed before this error","Reproduce by running the same linera-service command manually with the same arguments","Check for port conflicts between concurrently running tests (give each test unique ports)","Make the test fail fast on the first child error instead of issuing further operations against a dead child"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Rust: check the child is alive before issuing harness operations\nuse linera_service::util::ChildExt;\n\nchild.ensure_is_running()?; // fails fast with the exit status instead of a confusing later error\n// or, manually:\nif let Some(status) = child.try_wait()? {\n    anyhow::bail!(\"node exited early with {status}; inspect its stderr above\");\n}","typeGuard":null,"tryCatchPattern":"// Treat as symptom: surface the crash, do not retry operations against a dead child\nlet result = operation(&mut child).await;\nif let Err(e) = &result {\n    if e.to_string().contains(\"already exited with status\") {\n        // fail the test now; the root cause is in the child's earlier stderr\n        return Err(anyhow::anyhow!(\"child crashed earlier: {e}\").context(\"see child stderr\"));\n    }\n}","preventionTips":["Call ensure_is_running() right before each interaction so the crash is caught early with context","Capture and print child stderr/stdout in the harness so the real crash reason is visible","Give each test unique ports to avoid children killing each other","Abort the test on the first child error instead of continuing"],"tags":["process","subprocess","testing","test-harness"],"backgroundTag":"subprocess-exited-unexpectedly","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}