{"record":{"id":"11e188caaefbe694","repo":"Kuberwastaken/claurst","slug":"stdin-was-requested-as-piped","errorCode":null,"errorMessage":"stdin was requested as piped","messagePattern":"stdin was requested as piped","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-rust/crates/commands/src/new_move.rs","lineNumber":339,"sourceCode":"}\r\n\r\n/// Apply a captured patch to the destination worktree via `git apply -`.\r\nfn apply_changes(dest_root: &Path, patch: &str) -> Result<(), MoveError> {\r\n    use std::io::Write;\r\n    use std::process::Stdio;\r\n\r\n    let mut child = Command::new(\"git\")\r\n        .current_dir(dest_root)\r\n        .args([\"apply\", \"-\"])\r\n        .stdin(Stdio::piped())\r\n        .stdout(Stdio::piped())\r\n        .stderr(Stdio::piped())\r\n        .spawn()\r\n        .map_err(|e| MoveError::Apply(e.to_string()))?;\r\n\r\n    // Stream the patch on a separate thread so a large patch can't deadlock\r\n    // against git filling its stdout/stderr pipes.\r\n    let mut stdin = child.stdin.take().expect(\"stdin was requested as piped\");\r\n    let patch_owned = patch.to_string();\r\n    let writer = std::thread::spawn(move || {\r\n        let _ = stdin.write_all(patch_owned.as_bytes());\r\n        // `stdin` drops here, closing the pipe so git sees EOF.\r\n    });\r\n\r\n    let output = child\r\n        .wait_with_output()\r\n        .map_err(|e| MoveError::Apply(e.to_string()))?;\r\n    let _ = writer.join();\r\n\r\n    if output.status.success() {\r\n        Ok(())\r\n    } else {\r\n        Err(MoveError::Apply(git_stderr(&output, \"git apply failed\")))\r\n    }\r\n}\r\n\r","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/commands/src/new_move.rs#L321-L357","documentation":"This panic asserts that `git apply`'s stdin handle is available after spawning the child with `.stdin(Stdio::piped())`. Because piped stdin was explicitly requested, `child.stdin` is guaranteed to be `Some`, so the expect documents an invariant rather than handling a real failure. It can only fire if the spawn configuration was changed to not pipe stdin.","triggerScenarios":"Calling `apply_changes` (via `move_session_changes`) when the preceding `Command::new(\"git\").arg(\"apply\")...spawn()` was configured without `.stdin(Stdio::piped())`, making `child.stdin.take()` return None.","commonSituations":"A refactor of the git Command builder that removed or conditionally skipped `.stdin(Stdio::piped())`; someone switching stdin to `null()` or `inherit()`; copy-pasting the spawn block into another function without the pipe.","solutions":["Confirm the git Command still sets `.stdin(Stdio::piped())` before `.spawn()` — that is the invariant this expect guards.","If stdin configuration became dynamic, replace the expect with `ok_or`/`map_err` returning `MoveError::Apply(\"stdin not piped\".into())`.","Keep the pipe setup and the `stdin.take()` adjacent so future edits cannot silently decouple them.","Add a unit test that runs apply_changes against a temp git repo so a removed pipe fails the test instead of panicking in production."],"exampleFix":"// before\nlet mut stdin = child.stdin.take().expect(\"stdin was requested as piped\");\n// after\nlet mut stdin = child\n    .stdin\n    .take()\n    .ok_or_else(|| MoveError::Apply(\"git apply stdin was not piped\".into()))?;","handlingStrategy":"validation","validationCode":"// Assert the spawn configuration before relying on stdin:\nlet mut cmd = std::process::Command::new(\"git\");\ncmd.args([\"apply\", \"--whitespace=fix\"])\n    .stdin(std::process::Stdio::piped())\n    .stdout(std::process::Stdio::piped())\n    .stderr(std::process::Stdio::piped());\ndebug_assert!(true, \"stdin must remain piped for git apply\");","typeGuard":null,"tryCatchPattern":"// Panic, not Result — wrap apply_changes:\nlet res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| apply_changes(&repo, &patch)));\nif res.is_err() { /* report MoveError::Apply(\"internal: stdin not piped\") */ }","preventionTips":["Keep `.stdin(Stdio::piped())` on the same Command builder as the spawn, never split across helper calls","Add a regression test running apply_changes against a temp git repo","Prefer ok_or + error propagation over expect for child handle extraction","Review any Command-builder refactors for changed stdio settings"],"tags":["rust","git","process-spawn","stdin","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"b0637c97ec34144387cbf2f74f65df6d16a6cef1","analyzedAt":"2026-09-10T00:24:58.650Z","contentChangedAt":"2026-09-10T00:24:58.650Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}