Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Troubleshooting

madoqua: command not found

The wheel installs a binary onto your tool path. With uv tool install, check that ~/.local/bin is on PATH.

If the hook itself fails this way, remember that git runs hooks/pre-commit with your login PATH, not your shell’s. The shim madoqua install writes now looks in <repo>/.venv/bin first, so a dev-dependency install works without the venv active; a shim written by an older version only says exec madoqua run; run madoqua install again to replace it. Either way the binary has to be in the venv for that lookup to find it: uv add --dev madoqua.

madoqua: no virtualenv at …/.venv

madoqua only runs tools out of the repo’s own virtualenv. Create one and install what the checks call:

uv venv && uv sync

The verdict says (auto-activated .venv) every time

That is madoqua telling you .venv/bin was not the front of your PATH — it worked around that for the hook’s children, but everything else you type is using a different python. Activate the venv, or use uv run.

It says the same thing when the venv is on PATH but something sits in front of it: a version manager’s shim directory has no python of its own, so python still resolves into the venv while pytest comes from somewhere else.

A check ran a tool from outside the venv

It should not be able to: .venv/bin goes to the front of the PATH every tool inherits, and VIRTUAL_ENV names the repo’s venv, so a tool that a check spawns by name resolves there too. Older versions only rewrote PATH when python itself resolved elsewhere, which left a half-activated shell free to shadow everything else. If you still see it, check that the tool is installed in the venv at all — .venv/bin/<tool> --version.

…/.venv exists but its python is not the one that would run

Something on PATH shadows .venv/bin/python even after madoqua puts it first — usually a shim from a version manager. command -v python from the repo root will name it.

A check is slow and I do not know which

madoqua stats

Sorted by p95 descending, so the first row is the one worth fixing. Give that step a timeout_s while you work on it, or drop it for a single commit with MADOQUA_SKIP="<name>".

A failing check floods my terminal (or my agent’s context)

Cap it:

check = [{ name = "ty", cmd = "ty check", max_output_lines = 200 }]

The first 200 lines are kept and the rest becomes ... (N lines truncated).

`|` needs a shell, and madoqua runs commands directly

Commands are split with quote-aware whitespace rules and executed directly — there is no shell, so pipes, redirections and expansions are refused rather than passed along as literal arguments. Put the pipeline in a script and call the script.

The output is not what I expect

Run with --verbose (or RUST_LOG=debug) — logs go to stderr, so they never contaminate stdout:

madoqua --verbose run

cannot parse .../pyproject.toml

madoqua reads [tool.madoqua] out of the project’s pyproject.toml. A missing file, or a file with no [tool.madoqua] table, is fine — both mean “defaults”. Malformed TOML is not, and the error names the file. The same goes for hooks.local.toml.

Does it work in a git worktree?

Yes, and it uses the same overlay and the same timing log as the clone the worktree belongs to: a linked worktree’s .git is a file pointing at the clone’s metadata directory, and madoqua asks git where that is rather than joining .git/ onto the checkout. A log you configure yourself stays relative to the working tree you are committing in.

madoqua 0.2.2 and earlier joined the path literally, so every run in a linked worktree failed with cannot read …/.git/hooks.local.toml: Not a directory (os error 20) and exit 2, whether or not an overlay existed. Upgrade rather than working around it: the tools madoqua runs are the ones guarding the commit.

My tests’ git changed the real repository

A test suite that runs real git in a temporary directory — git init in tmp_path, then git config, git commit, git push — can end up operating on the repository you are committing to. git exports GIT_DIR and GIT_INDEX_FILE (and sometimes GIT_WORK_TREE, GIT_PREFIX, -c settings) to every hook, and a git that inherits GIT_DIR ignores the directory it is run in. From a linked worktree the damage is worst: core.bare = true and a user.name written into the clone’s config, branches replaced, junk commits pushed.

madoqua 0.2.4 and later remove those variables — everything git rev-parse --local-env-vars lists, plus GIT_NAMESPACE and GIT_CEILING_DIRECTORIES — from the environment of every tool it runs, and runs each tool from the repository root. GIT_AUTHOR_*, GIT_COMMITTER_* and GIT_SSH* are left alone. madoqua’s own git calls still see the hook’s environment, since they are about the repository being committed to.

On 0.2.3 and earlier, upgrade. If you have been bitten, check git config --local --list for core.bare and user.* entries you did not set, and git reflog for branches that moved. A test suite that must also run under other hook runners can protect itself by deleting those variables in a fixture before it touches git.

A commit went through with unformatted code

madoqua only sees git diff --cached. If a file was not staged, it was not checked. Note also that the fixers run on whole files: a file staged with git add -p is committed in full.