gastownhall/beads · error

git config --unset core.hooksPath failed: %w (output: %s)

Error message

git config --unset core.hooksPath failed: %w (output: %s)

What it means

FixHooksPath repairs a dangling core.hooksPath by running `git config --unset core.hooksPath` in the repo root. When the git subprocess exits non-zero, the error (including git's combined stderr/stdout output) is wrapped with this message. It indicates git itself refused or failed to remove the config value, not that the value was absent (an absent/dangling-free case returns nil earlier).

Source

Thrown at cmd/bd/doctor/git.go:693

		return nil // nothing set
	}

	if !IsBeadsManagedHooksPath(repoRoot, hooksPath) {
		return nil // never touch a third-party hooksPath
	}

	resolved := expandHookPathTilde(hooksPath)
	if !filepath.IsAbs(resolved) {
		resolved = filepath.Join(repoRoot, resolved)
	}
	if fileExists(resolved) {
		return nil // target exists — nothing dangling to fix
	}

	cmd := exec.Command("git", "config", "--unset", "core.hooksPath")
	cmd.Dir = repoRoot
	if output, err := cmd.CombinedOutput(); err != nil {
		return fmt.Errorf("git config --unset core.hooksPath failed: %w (output: %s)", err, strings.TrimSpace(string(output)))
	}
	return nil
}

// getConfiguredHooksPath reads the raw core.hooksPath value from repoRoot.
// The bool return is false when core.hooksPath is not set at all.
func getConfiguredHooksPath(repoRoot string) (string, bool) {
	cmd := exec.Command("git", "config", "--get", "core.hooksPath")
	cmd.Dir = repoRoot
	out, err := cmd.Output()
	if err != nil {
		return "", false
	}
	return strings.TrimSpace(string(out)), true
}

// IsBeadsManagedHooksPath reports whether hooksPath is one of the values bd
// itself writes to core.hooksPath (.beads/hooks or .beads-hooks, relative or

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run the command manually in the repo (`git config --unset core.hooksPath`) and read git's own error output.
  2. Confirm the working directory is a git repository (`git -C <repoRoot> rev-parse --git-dir`).
  3. Remove a stale lock: delete .git/config.lock if no git process is running.
  4. If the value is set globally/system-wide, unset it in that scope: `git config --global --unset core.hooksPath` or `git config --system --unset core.hooksPath`.
  5. If multiple values exist, use `git config --unset-all core.hooksPath`.

Example fix

// before (manual diagnosis)
$ git config --unset core.hooksPath
error: could not unset key in .git/config: permission denied
$ chmod u+w .git/config && git config --unset core.hooksPath

// after
$ bd doctor
hooksPath fix: OK
Defensive patterns

Strategy: validation

Validate before calling

if _, err := os.Stat(filepath.Join(repoRoot, ".git")); err != nil {
	return fmt.Errorf("not a git repository: %s", repoRoot)
}
out, err := exec.Command("git", "-C", repoRoot, "config", "--get", "core.hooksPath").Output()
if err == nil && len(out) > 0 {
	// hooksPath set; safe to run the fix
}

Try / catch

if err := FixHooksPath(repoRoot); err != nil {
	var execErr *exec.ExitError
	if errors.As(err, &execErr) {
		log.Printf("git refused unset (exit %d); run `git config --unset core.hooksPath` manually: %v", execErr.ExitCode(), err)
	}
	return err
}

Prevention

When it happens

Trigger: executing `git config --unset core.hooksPath` fails: the repo root path is wrong or not a git repository, the .git/config file is read-only or corrupted, the config is locked by another process, or core.hooksPath is set in a scope git cannot unset with the default flags (e.g. defined in a system/global config whose unset conflicts, or multiple values exist).

Common situations: Running `bd doctor` outside a real git work tree (cmd.Dir points at a non-repo); .git/config.lock left behind after a crashed git operation; the setting lives in ~/.gitconfig or /etc/gitconfig so a local unset misbehaves; permissions changed by a container running as a different user.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/33640921eedd9267. Report an issue: GitHub.