gitbutlerapp/gitbutler · error · anyhow::Error
pre-push hook failed: {}
Error message
pre-push hook failed: {} What it means
Thrown by but-workspace's legacy push path when a configured Git pre-push hook (or Husky-installed pre-push hook, when run_husky_hooks is on) exits non-zero, returning hooks::HookResult::Failure. The '{}' is the hook's captured stderr/error_data, so the message mirrors what the hook itself printed. The push is aborted before any ref update is attempted, exactly like `git push` being vetoed by the hook.
Source
Thrown at crates/but-workspace/src/legacy/push.rs:103
})?;
let before_sha = remote_before_sha(repo, remote_refname.as_ref())?;
let remote = repo.find_remote(remote_name.as_str())?;
let remote_url = remote
.url(gix::remote::Direction::Push)
.or_else(|| remote.url(gix::remote::Direction::Fetch))
.with_context(|| format!("Remote named {remote_name} didn't have a URL"))?;
if run_hooks {
match hooks::pre_push(
repo,
&remote_name,
&remote_url.to_bstring().to_str_lossy(),
local_sha.id,
&RemoteRefname::from_str(&remote_refname.as_bstr().to_str_lossy())?,
run_husky_hooks,
)? {
hooks::HookResult::Success | hooks::HookResult::NotConfigured => Ok(()),
hooks::HookResult::Failure(error_data) => Err(anyhow::anyhow!(
"pre-push hook failed: {}",
error_data.error
)),
}?;
}
let gerrit_push_args = gerrit_push_args(
gerrit_mode,
local_sha.id,
target_branch_name.as_bstr(),
&push_opts,
);
let push_output = push_with_askpass(
repo,
local_sha.id,
remote_refname.as_ref(),
with_force,
force_push_protection && !skip_force_push_protection,View on GitHub (pinned to caf1f223d3)
Solutions
- Read the text after 'pre-push hook failed:' — it is the hook's own output; fix whatever the hook reports (failing lint/test, missing tool).
- Re-run the hook manually with the same arguments it would receive (remote name, URL, refline 'local_sha remote_refname') to reproduce outside the push.
- If the hook is wrong or not wanted for this push, push with hooks disabled (the run_hooks=false / --no-verify equivalent) instead of deleting the hook.
- For husky hooks, ensure husky is installed (`husky install`) and the .husky/pre-push file is executable and has a shebang.
Example fix
# before: hook fails on unformatted code $ but push Error: pre-push hook failed: eslint --staged found 2 problems # after: fix what the hook flags, then push again $ npx eslint --fix . && git add -A $ but push
Defensive patterns
Strategy: try-catch
Validate before calling
// Rust: probe the hook before pushing
let hook = repo.path().join("hooks/pre-push");
if run_hooks && hook.exists() {
// expect it may fail; surface its output instead of a bare error
}
// or skip hooks entirely when the caller knows they are unwanted
workspace::legacy::push(/* run_hooks: false, run_husky_hooks: false */) Try / catch
match hooks::pre_push(...) {
Ok(HookResult::Failure(data)) => {
// treat as recoverable: show data.error, keep local commits intact
eprintln!("push vetoed by pre-push hook: {}", data.error);
}
Ok(_) => { /* proceed with push */ }
Err(e) => return Err(e), // infrastructure error, not a hook veto
} Prevention
- Keep pre-push hooks fast and dependency-light so they behave identically in every shell.
- In automation, pass the existing run_hooks=false option rather than deleting hooks.
- Test hooks by running them manually with the push refline arguments before relying on them.
When it happens
Trigger: Calling the workspace push API with run_hooks=true while .git/hooks/pre-push (or a husky .husky/pre-push) exists and exits non-zero — e.g. a linter/formatter/test step in the hook failing, or the hook script being missing its interpreter (env: ruby: No such file...).
Common situations: CI or local pre-push hooks running eslint/prettier/unit tests that fail; husky v4-style hooks still configured in package.json; a hook depending on tools not installed in the current shell (wrong PATH, nvm not loaded); pushing from an environment where the hook assumes a TTY.
Related errors
- Branch `{}` is pushed, but its remote ancestry does not matc
- Neither found push remote found in workspace nor unambiguous
- failed to find branch {target_branch_refname}
- GitForcePushProtection
- askpass broker must be initialized
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/5e94f45b36775823.
Report an issue: GitHub.