How to fix Cursor skipping git hooks from Source Control
Cursor 3.15.6 panel commits silently skip pre-commit and Husky hooks via a core.hooksPath override. Diagnose it in a minute and use the terminal or rollback workaround.
TL;DR: Since Cursor 3.15.6, Source Control commits skip all git hooks because the bundled extension sets core.hooksPath to the null device, so commit from the integrated terminal until a fix ships.
Commits from Cursor's Source Control panel started landing without lint, format, or secret checks in early August 2026, with no error and no --no-verify in the Git output. The same staged changes committed from the integrated terminal run hooks correctly. The cause is not Husky, not file permissions, and not your config: it is an environment override Cursor injects into every git child process, and it stays invisible to the usual git config checks. Related Cursor behavior is covered in the Cursor rules guide, the Cursor agent connection failures guide, and the clearing the Cursor cache guide, plus the sibling-error catalog at the Automation Error Index.
Why does Cursor skip git hooks since 3.15.6?
Cursor 3.15.6 shipped a change to its bundled git extension that pins four config keys for every git subprocess it spawns, including user-initiated commits. The list, found in extensions/git/dist/main.js, sets safe.bareRepository, core.fsmonitor to false, core.hooksPath to the null device, and core.attributesFile to the null device, as reported with the main.js snippet. On macOS and Linux the value resolves to /dev/null; on Windows it resolves to \\.\nul.
Because the override arrives through GIT_CONFIG_KEY_n and GIT_CONFIG_VALUE_n environment variables at command scope, it outranks every config file. Your repo can set core.hooksPath=.husky/_, the hook files can exist and be executable, and git still looks for hooks under the null device, finds nothing, and exits 0. The main tracking thread confirms the scope: pre-commit, commit-msg, pre-push, Husky, and plain .git/hooks scripts are all skipped, on macOS, Windows, and WSL2 Linux.
Pinning core.fsmonitor and core.attributesFile for background reads is reasonable. The bug is that core.hooksPath got swept into the same list and applied to commit as well. Stock VS Code has none of this machinery, which is why the same repo behaves correctly in VS Code. Git's own hooks documentation says pre-commit is bypassed only with --no-verify, and the panel sends no such flag, so the skip is silent by construction.

How do you confirm the panel is skipping hooks instead of a broken hook?
Run the always-fail test. Install a hook that cannot pass, stage a trivial change, and commit from the panel:
printf '#!/bin/sh\nexit 1\n' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
git add -A
# commit once from the Source Control panel, then:
git reset --soft HEAD~1
git commit -m "hook check"If the panel commit succeeds and the terminal commit is blocked, Cursor is skipping hooks. The timing gap backs it up: reporters logged 800 to 2100 ms per panel commit before August 7 and 60 to 90 ms after, while the identical terminal command took about 950 ms with hooks running. One reporter ran a 37-second test suite through pre-commit and watched the panel finish in 32 ms.

Do not trust git config --show-origin --get-all core.hooksPath here. Environment overrides never appear in that output, so the command reports your repo value and hides the command-scope value. To make git state the override directly, enable trace2 through global config, commit once from the panel, then remove the tracing:
git config --global trace2.normalTarget C:/path/to/trace2.log
git config --global trace2.configParams core.hooksPath
# commit once from the Source Control panel, then read the log
git config --global --unset-all trace2.normalTarget
git config --global --unset-all trace2.configParamsThe log shows two lines for the panel commit: scope:local core.hooksPath=.husky/_ followed by scope:command core.hooksPath=\\.\nul. Command scope wins. The only child process is git maintenance; no hook child ever starts. Terminal commits show the hook child starting as expected. See the git config scope documentation for why file-level settings cannot override this.
How do you fix Cursor skipping git hooks?
There is no in-panel toggle as of September 2026. Staff confirmed the issue is tracked but gave no timeline, and reporters found it still present on 3.16.17, 3.16.29, 3.17.8, and 3.17.21. Use one of these workarounds, ordered by least disruption:
- Commit from the integrated terminal. Stage in the panel if you like, then run
git commitin Cursor's terminal. Hooks run because the terminal does not inherit the extension'sGIT_CONFIG_*environment. This is the workaround staff recommends. - Roll back to 3.14.27. Multiple reporters confirmed panel commits run Husky hooks again after downgrading. Keep auto-update off until the fix lands, and re-run the always-fail test after every Cursor update.
- Run hooks manually before panel commits. If you must use the panel button, run the same checks the hook would run. For pre-commit frameworks this is usually
pre-commit run --all-filesor the package script behind your Husky hook, such aspnpm --silent format:staged. Treat a panel commit without a manual run as unverified. - Move enforcement to CI. Add the lint, format check, and secret scan as required CI jobs so a skipped local hook fails the pull request instead of landing silently. This does not restore local hooks, but it stops inconsistent trees from merging.
What does not help: setting core.hooksPath again in .git/config or global config, unsetting HUSKY=0, reinstalling Husky, or fixing file permissions. The override is applied after all of those, and it overwrites keys the caller already set. The Husky wrapper never executes, so Husky-specific debugging is a dead end until the panel path is fixed.
How do you verify hooks run again?
Re-run the always-fail hook after each change. The expected result is a blocked commit with hook output in the terminal, and a panel commit time back in the hundreds of milliseconds instead of tens. Then replace the test hook with your real hooks and make one normal commit from the path you intend to keep: terminal commits should show hook output inline, and git log --oneline -3 should list only commits that passed checks. Keep the trace2 lines from the earlier step as a reference: a fixed build should show no scope:command core.hooksPath entry for panel commits.
What if hooks still do not run from the terminal?
Then the problem is separate from this bug. Check the three classic causes in order. First, confirm the hook is executable with ls -l .git/hooks/pre-commit and that Husky's hook path matches git config --get core.hooksPath. Second, check for HUSKY=0 in the terminal environment and for a missing Node or pnpm on PATH when Cursor spawns the shell. Third, run the hook file directly with sh .git/hooks/pre-commit to see the real error outside git. If the terminal path works and only the panel skips, you are back in this bug and the workarounds above apply.
FAQ
Why does a panel commit take 60 ms while the terminal takes seconds?
The panel time is the commit without hooks. The terminal time is the commit plus the hook suite. A 10x to 30x gap on the same staged diff is the fastest tell that hooks were skipped.
Does this affect pre-push and commit-msg hooks too?
Yes. The override applies to every git child the extension spawns, so commit-msg, pre-push, and post-commit notification hooks tied to panel operations are affected the same way. Push from the terminal to keep pre-push checks.
Does downgrading to 3.14.27 really restore panel hooks?
Two independent reporters confirmed Husky and lint-staged hooks run from the panel again on 3.14.27. It is the only workaround that keeps the panel button, at the cost of missing newer Cursor changes.
Why does git config show no hooksPath override?
Because the value travels in process environment at command scope, not in a file. File-based inspection cannot see it. Only trace2 output from inside the spawned process shows the scope:command line.
Can I keep using the Source Control panel safely?
Only with a manual step: run hooks by hand before clicking commit, or gate merges on CI checks that repeat the hook suite. Without one of those, panel commits are equivalent to --no-verify even though the flag never appears.