{"record":{"id":"45de0a4a883f1b49","repo":"gitbutlerapp/gitbutler","slug":"configured","errorCode":null,"errorMessage":"configured","messagePattern":"configured","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/gitbutler-repo/src/hooks.rs","lineNumber":283,"sourceCode":"            prep.command = gix::path::from_bstring(with_slashes_for_bash.into_owned()).into();\n        }\n        prep.arg(remote_name).arg(remote_url)\n    })\n    .current_dir(repo.workdir().unwrap_or(repo.git_dir()))\n    .stdin(Stdio::piped())\n    .spawn()?;\n\n    {\n        let remote_commit = repo\n            .try_find_reference(&remote_tracking_branch.to_string())?\n            .map(|mut reference| reference.peel_to_id().map(|id| id.detach()))\n            .transpose()?\n            .unwrap_or_else(|| repo.object_hash().null());\n        // THIS IS WRONG: but is correct in the common case. This also is an issue when the ref is actually pushed,\n        // but we can fix it when moving everything to `gix`.\n        let local_tracking_branch_deduced =\n            format!(\"refs/heads/{}\", remote_tracking_branch.branch());\n        let stdin = child.stdin.as_mut().expect(\"configured\");\n        let refspec = format!(\n            \"{local_tracking_branch_deduced} {local_commit} {remote_tracking_branch} {remote_commit}\\n\"\n        );\n        // Hooks may exit before reading stdin if they don't need the refspec info.\n        // The actual success/failure is determined by the exit code via wait_with_output() below.\n        if let Err(err) = stdin.write_all(refspec.as_bytes())\n            && err.kind() != std::io::ErrorKind::BrokenPipe\n        {\n            return Err(err.into());\n        }\n    }\n\n    let output = child.wait_with_output()?;\n    if output.status.success() {\n        Ok(HookResult::Success)\n    } else {\n        let error = join_output(\n            output.stdout.to_str_lossy().into_owned(),","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-repo/src/hooks.rs#L265-L301","documentation":"run_pre_push() spawns the pre-push hook with .stdin(Stdio::piped()) (hooks.rs:270) and later grabs child.stdin with expect(\"configured\"). std::process::Child::stdin is Some only when the command was configured for piped stdin; since the spawn setup does exactly that a few lines above, None is impossible unless the construction is refactored. The code below already tolerates the hook exiting early (BrokenPipe is ignored).","triggerScenarios":"Any pre-push hook execution (pushing a repo that has .git/hooks/pre-push or .husky/pre-push). The panic fires only if the .stdin(Stdio::piped()) line is removed or changed in the Command construction above.","commonSituations":"Refactors of the hook-spawning code that drop or conditionalize piped stdin. Not user-triggerable: a fast-exiting hook is handled by the BrokenPipe check that follows.","solutions":["Keep .stdin(Stdio::piped()) on the Command used to spawn hooks","Replace the expect with an explicit error so future refactors fail loudly with context (see exampleFix)","Keep the existing hook integration tests (crates/gitbutler-repo/tests/repo/hooks.rs) green so spawn regressions surface there"],"exampleFix":"// before\nlet stdin = child.stdin.as_mut().expect(\"configured\");\n\n// after\nlet Some(stdin) = child.stdin.as_mut() else {\n    anyhow::bail!(\"pre-push hook was spawned without piped stdin\");\n};","handlingStrategy":"validation","validationCode":"// When spawning hook processes, make the stdin contract explicit up front\nlet mut cmd: std::process::Command = /* ... */;\ncmd.stdin(std::process::Stdio::piped()); // required: refspec is written to the hook\nassert_eq!(cmd.get_stdin(), Some(&std::process::Stdio::piped()));","typeGuard":null,"tryCatchPattern":"// Fail with context instead of panicking if the contract is broken\nlet Some(stdin) = child.stdin.as_mut() else {\n    anyhow::bail!(\"hook was spawned without piped stdin\");\n};","preventionTips":["Keep .stdin(Stdio::piped()) adjacent to the spawn call for hooks","Handle BrokenPipe on writes - hooks may exit before reading stdin (already done here)","Cover hook execution paths with integration tests"],"tags":["rust","child-process","stdio","git-hooks","pre-push","panic"],"backgroundTag":"missing-piped-stdin","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}