Graphify-Labs/graphify · error · RuntimeError

No git repository found at or above {path.resolve()}

Error message

No git repository found at or above {path.resolve()}

What it means

Raised by graphify's hooks install() when no .git directory can be found at or above the working path. Hook installation writes post-commit/post-checkout scripts into the repo's hooks directory (respecting core.hooksPath), so a git root is a hard prerequisite; _git_root returning None means the walk up from the given path never hit a repository boundary.

Source

Thrown at graphify/hooks.py:665

def _user_hooks_dir(hooks_dir: Path) -> Path:
    """Return the user-editable hooks directory.

    Husky 9 sets core.hooksPath to .husky/_ (wrapper scripts auto-generated by
    Husky), while user-editable hooks live in the parent .husky/. Return the
    parent when the resolved dir ends in '_' so install/status/uninstall target
    the correct location (#987).
    """
    if hooks_dir.name == "_":
        return hooks_dir.parent
    return hooks_dir


def install(path: Path = Path(".")) -> str:
    """Install graphify post-commit and post-checkout hooks in the nearest git repo."""
    root = _git_root(path)
    if root is None:
        raise RuntimeError(f"No git repository found at or above {path.resolve()}")

    hooks_dir = _user_hooks_dir(_hooks_dir(root))

    # Pin the current interpreter so the hook works even when the graphify
    # launcher is not on PATH at git-trigger time (uv tool / pipx isolation).
    # sys.executable is the Python running this very install command, so it is
    # always the correct isolated-venv interpreter.  The placeholder is replaced
    # in both scripts before writing; the allowlist in _pinned_python() strips
    # any characters unsafe in a shell path (empty result -> the pinned probe is
    # skipped), and import-verification catches a stale pinned path so it safely
    # falls through to the dynamic detection.
    pinned = _pinned_python()
    hook = _HOOK_SCRIPT.replace("__PINNED_PYTHON__", pinned)
    checkout = _CHECKOUT_SCRIPT.replace("__PINNED_PYTHON__", pinned)

    commit_msg = _install_hook(hooks_dir, "post-commit", hook, _HOOK_MARKER)
    checkout_msg = _install_hook(hooks_dir, "post-checkout", checkout, _CHECKOUT_MARKER)
    merge_msg = _register_merge_driver(root)

View on GitHub (pinned to 7fe58b0b0f)

Solutions

  1. cd into the actual repository root (verify with `git rev-parse --show-toplevel`) and re-run
  2. If the project isn't a repo yet: `git init` first, then `graphify hooks install`
  3. Pass an explicit path inside the repo: `graphify hooks install /path/to/repo`

Example fix

# before
cd ~/notes && graphify hooks install   # RuntimeError: No git repository found

# after
cd ~/myrepo && graphify hooks install
Defensive patterns

Strategy: validation

Validate before calling

import subprocess

def inside_git_repo(path: str = ".") -> bool:
    return subprocess.run(
        ["git", "-C", path, "rev-parse", "--git-dir"],
        capture_output=True,
    ).returncode == 0

if not inside_git_repo():
    raise SystemExit("not a git repository - run `git init` or cd to the repo root")

Try / catch

try:
    install(Path("."))
except RuntimeError as e:
    if "No git repository found" in str(e):
        raise SystemExit("cd to a git repo, or `git init` first")
    raise

Prevention

When it happens

Trigger: Running `graphify hooks install` (default path '.') in a plain directory, a fresh project before `git init`, a subdirectory of an archive extract, or a path where .git was deleted; also when GIT_DIR semantics are unusual (worktree/submodule edge cases the walker doesn't recognize).

Common situations: Trying hooks in a scratch/docs folder; running the command from the wrong terminal cwd; sandboxed environments that strip .git; shallow automation clones that remove .git to save space.

Related errors


AI-assisted analysis of Graphify-Labs/graphify@7fe58b0b0f (2026-08-14). Data as JSON: /api/errors/56d596907e630125. Report an issue: GitHub.