#1548·lefthook

Generated hook shim exits 0 when the lefthook binary is missing, silently skipping configured hooks (fail-open)

Author: liqdmetalCreated Sep 17, 2026Updated Sep 17, 2026

Description

The generated hook shim fails open: when the lefthook binary cannot be found, the shim prints Can't find lefthook in PATH and exits 0, so git proceeds and the repo's configured hooks are silently skipped. For a pre-push gate this means a broken environment disables the gate with no failure signal.

Generated with lefthook install v1.13.6 (template internal/templates/hook.tmpl, unmodified output).

Reproduction

  1. Create a repo with a lefthook.yml defining any pre-push command.
  2. Remove the lefthook binary from PATH (simulate a broken/uninstalled environment).
  3. Run the generated pre-push hook the way git does:
bash
env -i PATH=/usr/bin HOME="$HOME" /path/to/generated/pre-push origin fake
echo $?
# Can't find lefthook in PATH
# 0   <-- hook "passed", push proceeds, hooks never ran

Expected behavior

When a lefthook config exists in the repository but no binary can be found, the hook should abort the git operation (exit 1) rather than silently succeed — the same fail-closed contract that assert_lefthook_installed: true renders into the hook.

Repos without a lefthook config should keep the current silent no-op, since a shared core.hooksPath can serve many repos.

Why the existing knob doesn't cover this

assert_lefthook_installed is opt-in and baked in at install time: enabling it later still leaves the previously generated hooks fail-open until every repo re-runs lefthook install, and repos that adopt a lefthook.yml after their hooks were generated are permanently fail-open. A config-existence check in the shim's not-found branch covers both at runtime.

Suggested fix (verified locally on 1.13.6 output)

In hook.tmpl's not-found else branch:

bash
else
  echo "Can't find lefthook in PATH"
  dir="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
  # Fail closed when this repo actually uses lefthook; stay a no-op otherwise.
  if test -f "$dir/lefthook.yml" || test -f "$dir/.lefthook.yml" || \
     test -f "$dir/lefthook.yaml" || test -f "$dir/.config/lefthook.yml"
  then
    echo "ERROR: Operation is aborted due to lefthook settings."
    echo "Make sure lefthook is available in your environment and re-try."
    echo "To skip these checks use --no-verify git argument or set LEFTHOOK=0 env variable."
    exit 1
  fi
fi

Verified paths after patching a local shim: not-found + config present → exit 1 (git aborts); not-found + no config → exit 0 (no-op preserved); LEFTHOOK=0 escape hatch still honored; binary present → normal execution.

Related in spirit: #1471 (stage_fixed failures also report success).