From the first week of August to late September 2026, every commit made through Cursor's Source Control panel ran without your git hooks. No error. No warning. No --no-verify anywhere in the logged command. The commit just succeeded, faster than it used to, and the easiest way to notice was that it stopped feeling slow.
Cursor fixed it in 3.22, which staff announced as available on 25 September 2026. Staff say the latest 3.22 build, and anything newer, runs your hooks again. The open question is what got committed during those seven weeks, and that's the part of this post still worth your time. (Updated 2026-11-02; first published 2026-08-27 while the bug was open.)
TL;DR
Cursor 3.15.6 began injecting core.hooksPath, pointed at the platform's null device, into every git call the Source Control panel made. Git found no hooks and ran none: pre-commit, commit-msg, pre-push, and everything they invoke. Cursor 3.22 (available 25 September 2026) restored them. Check your version, run the 30-second test below, and if you relied on a secret-scanning hook between early August and your update, scan the history for what slipped past it.
What is actually happening
Cursor's bundled git extension appended config overrides to each git invocation using GIT_CONFIG_KEY_n / GIT_CONFIG_VALUE_n environment variables. One of them set core.hooksPath to Node's os.devNull, which is /dev/null on macOS and Linux and \\.\nul on Windows. Git dutifully looked for hooks in a place where there were none.
A Cursor staff member confirmed the mechanism directly. Responding on 2026-08-10 in a second thread, to a user who had decompiled the bundled extension:
The decompilation and your conclusion about the
GIT_CONFIG_*env vars are spot on, that's exactly what's happening.
A second staff member, the same day, in the main bug thread:
Thanks for the detailed report, what you're seeing isn't intended. Source Control Commit is skipping hooks right now; the same commit from the terminal should still run them. We've let the team know and this is an issue we're tracking.
Worth being precise about intent: core.hooksPath was not the only thing in that injected array. Reporters also found safe.bareRepository, core.fsmonitor and core.attributesFile alongside it, which read like a hardening patch. On 11 September a Cursor staff member confirmed that, and explained why the fix was not a one-line delete:
That override was added to stop agent-run git from executing hooks out of untrusted repo config, and it got applied too broadly, to manual Source Control commits as well. A proper fix keeps that protection for agent-initiated git while letting your own commits run hooks, so it's a scoping change rather than a one-liner.
So the hook-skipping was deliberate for the agent and accidental for you. Keep the first half of that in mind; it comes back below.
We tested the mechanism
The forum reports describe the symptom. We wanted to confirm that core.hooksPath set to the null device is sufficient on its own, and, more importantly, whether a developer could tell afterwards.
Git 2.43.0, a fresh repo, and a pre-commit hook that does nothing but refuse:
#!/bin/sh
echo "PRE-COMMIT HOOK RAN"
exit 1
$ git commit -m one
PRE-COMMIT HOOK RAN
exit=1
$ git -c core.hooksPath=/dev/null commit -m two
[master (root-commit) 2c399a8] two
exit=0
$ GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.hooksPath \
GIT_CONFIG_VALUE_0=/dev/null git commit -m three
[master 1666cc0] three
exit=0
Both bypass forms create the commit, exit 0, and print nothing about the hook. That part matches the reports. Here is the part that makes this worth writing about:
$ git config --get core.hooksPath
$ echo $?
1
Nothing. Exit 1, meaning the key is not set. The override lives for the duration of one command and leaves no trace in your global config, your repo config, or anywhere else you would think to look. A developer who suspects something is wrong and goes hunting through git config will find their setup is fine, because it is.
This is why it ran for seven weeks before a fix shipped, and why it's worth checking rather than assuming. There is no artifact. The only evidence is a hook that did not fire, and a hook not firing looks exactly like a hook that had nothing to say.
What it turns off
Every git hook, and with them everything those hooks invoke. In practice, for this audience, that is:
| What you set up | What it was doing | Now |
|---|---|---|
pre-commit running gitleaks or trufflehog | Blocking a commit that contains an API key | Does not run |
| Husky-managed chains | Whatever the chain does, in order | Does not run |
lint-staged | Formatting and linting staged files | Does not run |
commit-msg | Enforcing conventional commit format | Does not run |
pre-push | Last check before code leaves your machine | Does not run |
Husky is a hook manager, not a hook. It installs a pre-commit file, and that file is what git is no longer finding. Nothing in your Husky config needs changing, which is exactly why people spend an afternoon changing it.
The one a security-minded reader should care about is the first row. A reporter on 26 August named it directly:
This is especially dangerous because Git hooks may enforce formatting, linting, commit-message validation, secret scanning, or other repository policies.
Scope that honestly, because the scope matters. The check was bypassed only for commits made through Cursor's Source Control panel, on 3.15.6 up to 3.22. Terminal commits are unaffected. Other git clients are unaffected. GitHub push protection, and any secret scanning that runs in CI, are unaffected. Nobody has reported an actual secret reaching a repository through this.
And a local pre-commit hook was never your strongest control anyway. Anyone can pass --no-verify on any day of the week. But it's the control most commonly recommended to founders shipping AI-built code, it's the one most people believe is watching, and for seven weeks, for the click most people use, it was off.
Check your own setup in sixty seconds
In your repo, create a hook that refuses everything:
printf '#!/bin/sh\nexit 1\n' > .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
If you use Husky, point at its directory instead: .husky/pre-commit.
Stage any trivial change, then commit from the Source Control panel, the checkmark button in the sidebar.
On 3.22 or later the panel should refuse the commit. If it succeeds, your hooks are being skipped: check Help > About for your version and update.
Run the same commit from the integrated terminal with git commit -m test. It should be blocked too. If the terminal blocks it and the panel doesn't, you're on a build with the bug.
Delete the test hook. Then decide what to do about the gap.
What to do about it
Update to the latest 3.22 build or anything newer. That's the fix. One side effect worth knowing: a Cursor staff member explained on 27 September that the panel waits for everything a hook starts before it marks the commit done. In that thread the reporter's spinning Commit button turned out to be a slow post-commit hook that had silently not run for weeks. That's your hook working, not a new bug. If you can't update, committing from the integrated terminal runs hooks on any version, which is the workaround staff gave while the bug was open.
Don't count on hooks for the agent's commits. Cursor staff said the override exists to stop agent-run git from executing repo hooks, and that the fix would keep that protection for agent-initiated git; the 3.22 announcement describes the fix only for actions started from the Source Control view. We haven't been able to test what shipped, so check it yourself: leave the exit 1 hook in place and ask the agent to commit something. If the commit lands, a gitleaks hook would not have stopped an agent from committing a key either. That's a reasonable security choice on Cursor's part (a cloned repo's hooks are code you didn't write), and it's one more reason the check below can't live only on your laptop.
Move secret scanning off your laptop. The durable fix here has nothing to do with Cursor. A control that lives in .git/hooks is opt-in per machine, skippable with one flag, absent on a fresh clone, and, as this shows, disableable by your editor without telling you. Put the same check somewhere it cannot be waved through:
- GitHub push protection rejects the push itself, server-side. Free on public repositories.
- Gitleaks in CI runs on the pull request, on GitHub's machines, whatever your editor did.
- Both together, with the pre-commit hook kept as the fast local convenience it always was.
Our git secret scanning guide covers setting those up. The short version: if losing one local hook meaningfully weakens your position, the hook was carrying more weight than it should have been.
Check what already shipped. If you committed through the panel between early August and your 3.22 update and you relied on that hook, the useful question isn't whether the hook runs today. It's whether anything got through in the meantime. Scanning git history for secrets is a different job from blocking new ones, and this is the situation it exists for.
If you find a key that shipped, rotating it comes first and rewriting history comes second. A key that reached a remote must be treated as public, whether or not you can scrub the commit.
Is this a VS Code bug?
No, and it is worth being clear about that since Cursor is a VS Code fork.
We checked the upstream source. The git extension in microsoft/vscode sets exactly one environment variable, GIT_OPTIONAL_LOCKS=0. The strings hooksPath, GIT_CONFIG_KEY and devNull do not appear in extensions/git/src/git.ts or anywhere else in that repository, and there is no equivalent upstream issue. This is a change in Cursor's fork.
Where it stands
Fixed. In the main thread, users kept reporting the bug through September, including on 3.20.21 and 3.20.17 (16 and 17 September), and a separate report on 23 September found the same injection in 3.21.16. On 22 September a Cursor staff member said a fix was coming in 3.22, the next minor release. On 25 September they announced 3.22 was available, and on 28 September another staff member wrote that "the fix for Source Control commits skipping hooks has landed and is shipping in the 3.22 line." The thread auto-closed on 31 October. Nobody reported hooks still being skipped on 3.22, though nobody posted to confirm the fix either, and Cursor's public changelog doesn't mention it, so the staff posts are the only source.
On the version that introduced it: a release tracker polling Cursor's update endpoint shows 3.14.27 was still the stable build on 5 August, and the first forum report of 3.15.6 arrived on 7 August. So it landed somewhere in that window. Cursor's public changelog does not list patch versions, so there is no official release date to cite, and its August entries do not mention the git extension at all.
As of 2026-11-02 this bug is fixed in Cursor 3.22 and later. The fix lives in the Cursor release, not in anything you configure, so the only action is updating. The original thread is closed; if hooks stop running from the panel again on a current build, that's a new bug and worth its own report.
Why is my pre-commit hook not running in Cursor?
If you're on a Cursor version from 3.15.6 up to before 3.22, commits made through the Source Control panel run with core.hooksPath pointed at the platform's null device, so git finds no hooks to run. Cursor shipped the fix in 3.22, which staff announced as available on 2026-09-25. Update, then rerun the exit 1 test above. If you're already on 3.22 or later and hooks still don't run, the cause is something else: check git config core.hooksPath and that the hook file is executable.
Is Husky broken in Cursor?
Husky itself is fine. It installs a pre-commit hook, and that hook is what gets skipped. The same goes for anything a hook invokes, including lint-staged and gitleaks. Nothing in your Husky config needs changing, which is the frustrating part.
Which Cursor versions are affected?
Every version from 3.15.6, which reached the stable channel in the first week of August 2026, up to the 3.22 release. Users reported it in builds as late as 3.20.21 and 3.21.16. Cursor staff announced 3.22 as available on 2026-09-25 and said on 2026-09-28 that the fix is shipping in the 3.22 line. Check Help > About for your version.
How do I check whether my hooks are running?
Add a pre-commit hook containing only exit 1, stage a change, and commit from the Source Control panel. On 3.22 or later the commit should be refused. If it succeeds, hooks are being skipped and you're probably on an older build. Delete the hook afterwards.
Does this affect VS Code?
No. The git extension in microsoft/vscode sets one environment variable, GIT_OPTIONAL_LOCKS. The strings hooksPath, GIT_CONFIG_KEY and devNull do not appear in that extension's source at all, so this is a change in Cursor's fork rather than upstream behaviour.
Did Anything Slip Through?
CheckYourVibe reads your deployed app the way a stranger would and flags the keys that made it into the bundle, whatever your editor did on the way there.