{"record":{"id":"df4512cc3fa51b70","repo":"slint-ui/slint","slug":"child-has-no-stdout","errorCode":null,"errorMessage":"Child has no stdout","messagePattern":"Child has no stdout","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/editor-preview/child_process.rs","lineNumber":54,"sourceCode":"\n    fn preview_is_running(&self) -> bool {\n        self.inner.borrow().as_ref().map(|i| !i.communication_handle.is_finished()).unwrap_or(false)\n    }\n\n    fn start_preview(&self) -> crate::Result<()> {\n        if let Some(inner) = self.inner.borrow_mut().take() {\n            inner.communication_handle.abort();\n        }\n\n        let mut child = tokio::process::Command::new(&self.executable)\n            .args(&self.arguments)\n            .stdin(std::process::Stdio::piped())\n            .stdout(std::process::Stdio::piped())\n            .spawn()?;\n\n        tracing::debug!(\"Preview process spawned (PID {:?})\", child.id());\n\n        let from_child = child.stdout.take().expect(\"Child has no stdout\");\n        let mut to_child = child.stdin.take().expect(\"Child has no stdin\");\n\n        let channel = self.preview_to_lsp_channel.clone();\n\n        let communication_handle = tokio::spawn(async move {\n            let _exited_guard = scopeguard::guard(channel.clone(), |channel| {\n                channel.send(PreviewToLspMessage::Exited).ok();\n            });\n            let reader = tokio::io::BufReader::new(from_child);\n            let mut lines = reader.lines();\n            while let Some(line) = lines.next_line().await.map_err(|e| e.to_string())? {\n                if let Ok(message) = serde_json::from_str(&line) {\n                    channel.send(message).map_err(|e| e.to_string())?;\n                }\n            }\n\n            let exit_status = child.wait().await.map_err(|e| e.to_string());\n","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/slint-ui/slint/blob/bb937076de3f7919766c1f25e2e969367cf77e9a/internal/editor-preview/child_process.rs#L36-L72","documentation":"The editor-preview LSP spawns its preview child process with `stdout(Stdio::piped())`, then takes ownership of the stdout pipe. `child.stdout.take()` returning None means the pipe was not configured or already taken — an internal invariant break in the spawn setup. The panic aborts the LSP server's preview thread.","triggerScenarios":"`start_preview` (called from `send`) spawning the preview binary when the `Stdio::piped()` configuration did not take effect or stdout was already consumed — practically only from a code regression in spawn options or an already-taken handle.","commonSituations":"Custom/patched builds where spawn options were modified; reusing a `Child` whose stdout was already taken; extremely unlikely in stock builds.","solutions":["Ensure the spawn call keeps `.stdout(std::process::Stdio::piped())`","Take stdout exactly once and keep the returned reader alive","Rebuild/reinstall the slint-lsp binary from a clean checkout","Report a bug with version info if reproducible in an unmodified build"],"exampleFix":"// before (stdout taken twice)\nlet a = child.stdout.take().expect(\"Child has no stdout\");\nlet b = child.stdout.take().expect(\"Child has no stdout\");\n\n// after\nlet a = child.stdout.take().expect(\"Child has no stdout\");\n// do not take again; use `a`","handlingStrategy":"validation","validationCode":"let Some(stdout) = child.stdout.take() else { return Err(\"preview child stdout missing\"); };","typeGuard":"fn has_pipes(child: &std::process::Child) -> bool { child.stdout.is_some() && child.stdin.is_some() }","tryCatchPattern":"// wrap start_preview\nmatch start_preview().await {\n    Ok(h) => h,\n    Err(e) => tracing::error!(\"preview failed to start: {e}\"),\n}","preventionTips":["Always spawn with Stdio::piped() for both stdin and stdout","Take each pipe handle exactly once, immediately after spawn","Spawn a fresh Child per preview session; never reuse handles"],"tags":["lsp","process","editor-preview","panic"],"backgroundTag":"internal-invariant-violation","analyzedSha":"bb937076de3f7919766c1f25e2e969367cf77e9a","analyzedAt":"2026-09-16T01:37:20.251Z","contentChangedAt":"2026-09-16T01:37:20.251Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}