gastownhall/beads · error

%s is tracked by git and not a bd-managed hook; bd will not

Error message

%s is tracked by git and not a bd-managed hook; bd will not modify committed files it does not own
Untrack it (git rm --cached) or move hooks to an untracked directory and re-run

What it means

bd refuses to modify a git-tracked hook file that it does not own. Writing into a foreign committed file would dirty every clone sharing the repo (the wy-81fnur incident), so guardHookWritePath blocks the write when `git ls-files` shows the hook is tracked and the file lacks bd's section markers or legacy bd hook signature. This is a deliberate safety policy, not a transient failure.

Source

Thrown at cmd/bd/hooks.go:837

	}
	if fi.Mode()&os.ModeSymlink != 0 {
		target := "unresolvable target"
		if resolved, rerr := filepath.EvalSymlinks(hookPath); rerr == nil {
			target = resolved
		} else if link, lerr := os.Readlink(hookPath); lerr == nil {
			target = link
		}
		return fmt.Errorf("%s is a symlink to %s; writing would rewrite the link target, not the hook\nRemove the symlink (or leave that hook to its owner) and re-run", hookPath, target)
	}
	if allowTracked {
		return nil
	}
	// A tracked file is refused only when bd does NOT own it: writing into a
	// foreign tracked file dirties every clone that shares it (the wy-81fnur
	// incident). A bd-owned hook the user chose to commit (e.g. a team-shared
	// .beads/hooks/) is bd's to maintain — same policy as shared installs.
	if isGitTrackedFile(hookPath) && !isBdOwnedHookFile(hookPath) {
		return fmt.Errorf("%s is tracked by git and not a bd-managed hook; bd will not modify committed files it does not own\nUntrack it (git rm --cached) or move hooks to an untracked directory and re-run", hookPath)
	}
	return nil
}

// isBdOwnedHookFile reports whether the hook file at path is bd-managed:
// either section-marker format or a legacy bd hook (shim or inline).
func isBdOwnedHookFile(path string) bool {
	content, err := os.ReadFile(path) // #nosec G304 -- path is a hook location bd resolved
	if err != nil {
		return false
	}
	if strings.Contains(string(content), hookSectionBeginPrefix) {
		return true
	}
	versionInfo, err := getHookVersion(path)
	return err == nil && versionInfo.IsBdHook
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run `git rm --cached <hookPath>` to untrack the hook while keeping it on disk, then re-run the bd command
  2. Move your custom hooks to an untracked directory (e.g. .beads/hooks/ or .githooks/) and re-run bd hooks install
  3. If the hook is actually bd-generated (has bd section markers), no action needed — bd allows tracked bd-owned hooks; verify with a `git rm --cached` only if bd still refuses

Example fix

// before: hook tracked and foreign
$ git add .git/hooks/pre-commit && bd hooks install
error: ... tracked by git and not a bd-managed hook
// after
$ git rm --cached .git/hooks/pre-commit
$ bd hooks install
Hooks installed.
Defensive patterns

Strategy: validation

Validate before calling

tracked=$(git -C "$(dirname "$hook")" ls-files --error-unmatch -- "$(basename "$hook")" 2>/dev/null && echo yes || echo no); [ "$tracked" = yes ] && git rm --cached "$hook" && echo untracked

Type guard

func isHookSafeToWrite(hookPath string) bool { return !isGitTrackedFile(hookPath) || isBdOwnedHookFile(hookPath) }

Prevention

When it happens

Trigger: Running `bd hooks install` (via installHooksWithOptions -> guardHookWritePath) or applyHookMigrationExecution when the target hook path (e.g. .git/hooks/pre-commit or .beads/hooks/pre-commit) is committed to git and its content is not bd-generated.

Common situations: Teams commit custom pre-commit/pre-push hooks and later run bd hooks install into the same directory; a hook copied from another tool was added with `git add`; switching hooks directories while the old committed hooks still exist.

Related errors


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