{"record":{"id":"7962137ed6aec587","repo":"Hmbown/CodeWhale","slug":"lane-child-stderr-was-not-piped","errorCode":null,"errorMessage":"lane child stderr was not piped","messagePattern":"lane child stderr was not piped","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/lane/src/runtime.rs","lineNumber":485,"sourceCode":"    child_command.args(&command[1..]);\n    child_command.envs(environment);\n    child_command.stdout(Stdio::piped()).stderr(Stdio::piped());\n    let mut child = match child_command.spawn() {\n        Ok(child) => child,\n        Err(error) => {\n            return fail(\n                anyhow::Error::from(error).context(format!(\"spawn lane child command {command:?}\")),\n                127,\n            );\n        }\n    };\n    let stdout = match child.stdout.take() {\n        Some(stdout) => stdout,\n        None => return fail(anyhow::anyhow!(\"lane child stdout was not piped\"), 125),\n    };\n    let stderr = match child.stderr.take() {\n        Some(stderr) => stderr,\n        None => return fail(anyhow::anyhow!(\"lane child stderr was not piped\"), 125),\n    };\n    let stdout_log = log_path.clone();\n    let stderr_log = log_path.clone();\n    let stdout_thread = thread::spawn(move || stream_child_output(stdout, stdout_log, \"stdout\"));\n    let stderr_thread = thread::spawn(move || stream_child_output(stderr, stderr_log, \"stderr\"));\n\n    let status = match child.wait() {\n        Ok(status) => status,\n        Err(error) => {\n            return fail(\n                anyhow::Error::from(error).context(format!(\"wait for lane child {command:?}\")),\n                LANE_PROXY_FAILURE_EXIT_CODE,\n            );\n        }\n    };\n    let stdout_result = stdout_thread\n        .join()\n        .map_err(|_| anyhow::anyhow!(\"lane proxy stdout logger panicked\"))?;","sourceCodeStart":467,"sourceCodeEnd":503,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/lane/src/runtime.rs#L467-L503","documentation":"The sibling guard for stderr in run_lane_log_proxy: after spawning the lane child with .stderr(Stdio::piped()), child.stderr.take() returned None, so the proxy cannot stream the child's stderr into the lane log. It appends a lane_proxy_failure event and publishes exit receipt 125. Like the stdout variant it is an internal invariant guard that should be unreachable with the current spawn configuration.","triggerScenarios":"The .stderr(Stdio::piped()) configuration is missing or removed before spawn; the Child was constructed by code that configured stderr as inherit/null; a platform quirk yields a successful spawn without a stderr pipe.","commonSituations":"Refactors that split child-command construction; helpers that return a Child rebuilt without stderr piped; downstream merges that touch the spawn block at runtime.rs:466-478.","solutions":["Confirm .stderr(Stdio::piped()) is still chained before child_command.spawn() at crates/lane/src/runtime.rs:469","If the Command is assembled in another function, keep the piped-stdio configuration at the spawn site and assert it","Add/keep a regression test that exercises run_lane_log_proxy end-to-end so any stdio change is caught","Read the lane log's lane_proxy_error entry to confirm the 125 exit path"],"exampleFix":"// before\nchild_command.stdout(Stdio::piped());\nlet mut child = child_command.spawn()?;\n// after\nchild_command.stdout(Stdio::piped()).stderr(Stdio::piped());\nlet mut child = child_command.spawn()?;","handlingStrategy":"validation","validationCode":"let mut cmd = Command::new(&command[0]);\ncmd.args(&command[1..]).stdout(Stdio::piped()).stderr(Stdio::piped());\nlet mut child = cmd.spawn().context(\"spawn lane child\")?;\ndebug_assert!(child.stderr.is_some());","typeGuard":"fn piped_stderr(child: &Child) -> Option<&ChildStderr> { child.stderr.as_ref() }","tryCatchPattern":"let stderr = match child.stderr.take() {\n    Some(s) => s,\n    None => return fail(anyhow!(\"lane child stderr was not piped\"), 125),\n};","preventionTips":["Chain .stdout(Stdio::piped()).stderr(Stdio::piped()) immediately before spawn","Never let the Child pass through code that resets stdio to inherit/null","Cover the proxy path with a round-trip test that asserts both streams reach the lane log"],"tags":["rust","process-spawn","stdio","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}