gitbutlerapp/gitbutler · info
configured
Error message
configured
What it means
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).
Source
Thrown at crates/gitbutler-repo/src/hooks.rs:283
prep.command = gix::path::from_bstring(with_slashes_for_bash.into_owned()).into();
}
prep.arg(remote_name).arg(remote_url)
})
.current_dir(repo.workdir().unwrap_or(repo.git_dir()))
.stdin(Stdio::piped())
.spawn()?;
{
let remote_commit = repo
.try_find_reference(&remote_tracking_branch.to_string())?
.map(|mut reference| reference.peel_to_id().map(|id| id.detach()))
.transpose()?
.unwrap_or_else(|| repo.object_hash().null());
// THIS IS WRONG: but is correct in the common case. This also is an issue when the ref is actually pushed,
// but we can fix it when moving everything to `gix`.
let local_tracking_branch_deduced =
format!("refs/heads/{}", remote_tracking_branch.branch());
let stdin = child.stdin.as_mut().expect("configured");
let refspec = format!(
"{local_tracking_branch_deduced} {local_commit} {remote_tracking_branch} {remote_commit}\n"
);
// Hooks may exit before reading stdin if they don't need the refspec info.
// The actual success/failure is determined by the exit code via wait_with_output() below.
if let Err(err) = stdin.write_all(refspec.as_bytes())
&& err.kind() != std::io::ErrorKind::BrokenPipe
{
return Err(err.into());
}
}
let output = child.wait_with_output()?;
if output.status.success() {
Ok(HookResult::Success)
} else {
let error = join_output(
output.stdout.to_str_lossy().into_owned(),View on GitHub (pinned to caf1f223d3)
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
Example fix
// before
let stdin = child.stdin.as_mut().expect("configured");
// after
let Some(stdin) = child.stdin.as_mut() else {
anyhow::bail!("pre-push hook was spawned without piped stdin");
}; Defensive patterns
Strategy: validation
Validate before calling
// When spawning hook processes, make the stdin contract explicit up front let mut cmd: std::process::Command = /* ... */; cmd.stdin(std::process::Stdio::piped()); // required: refspec is written to the hook assert_eq!(cmd.get_stdin(), Some(&std::process::Stdio::piped()));
Try / catch
// Fail with context instead of panicking if the contract is broken
let Some(stdin) = child.stdin.as_mut() else {
anyhow::bail!("hook was spawned without piped stdin");
}; Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- broker already configured
- broker has not been configured
- BUG: {id} is queued
- BUG: It should not be possible to omit sources
- prefiltered
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/45de0a4a883f1b49.
Report an issue: GitHub.