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

  1. 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).
  2. 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.
  3. 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.
  4. 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

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


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/5e94f45b36775823. Report an issue: GitHub.