[{"data":1,"prerenderedAt":596},["ShallowReactive",2],{"blog-how-to/cursor-pre-commit-hooks-not-running":3},{"id":4,"title":5,"body":6,"category":563,"date":564,"dateModified":564,"description":565,"draft":566,"extension":567,"faq":568,"featured":566,"headerVariant":578,"image":579,"keywords":580,"meta":581,"navigation":584,"ogDescription":585,"ogTitle":579,"path":586,"readTime":587,"schemaOrg":588,"schemaType":589,"seo":590,"sitemap":591,"stem":592,"tags":593,"twitterCard":594,"__hash__":595},"blog/blog/how-to/cursor-pre-commit-hooks-not-running.md","Cursor Pre-Commit Hooks Not Running: The Source Control Bug (2026)",{"type":7,"value":8,"toc":552},"minimark",[9,18,21,31,36,62,73,83,92,97,115,119,125,128,142,151,154,163,170,176,180,183,266,272,275,280,283,289,293,313,324,330,340,346,350,356,366,385,393,404,410,414,417,442,446,449,452,463,523,540],[10,11,12,13,17],"p",{},"If you commit through the Source Control panel in Cursor, your pre-commit hook has not run since the first week of August. No error. No warning. No ",[14,15,16],"code",{},"--no-verify"," anywhere in the logged command. The commit just succeeds, faster than it used to, and the fastest way to notice is that it stopped feeling slow.",[10,19,20],{},"Cursor staff confirmed it on 10 August 2026. As of today it is still there.",[22,23,24],"tldr",{},[10,25,26,27,30],{},"Cursor 3.15.6 began injecting ",[14,28,29],{},"core.hooksPath",", pointed at the platform's null device, into every git call the Source Control panel makes. Git then finds no hooks and runs none: pre-commit, commit-msg, pre-push, and everything they invoke. Commit the same staged changes from the integrated terminal and your hooks run normally. That is the workaround Cursor staff gave on 12 August, and there is still no fix.",[32,33,35],"h2",{"id":34},"what-is-actually-happening","What is actually happening",[10,37,38,39,42,43,46,47,49,50,53,54,57,58,61],{},"Cursor's bundled git extension appends config overrides to each git invocation using ",[14,40,41],{},"GIT_CONFIG_KEY_n"," / ",[14,44,45],{},"GIT_CONFIG_VALUE_n"," environment variables. One of them sets ",[14,48,29],{}," to Node's ",[14,51,52],{},"os.devNull",", which is ",[14,55,56],{},"/dev/null"," on macOS and Linux and ",[14,59,60],{},"\\\\.\\nul"," on Windows. Git dutifully looks for hooks in a place where there are none.",[10,63,64,65,72],{},"A Cursor staff member confirmed the mechanism directly. Responding on 2026-08-10 in ",[66,67,71],"a",{"href":68,"rel":69},"https://forum.cursor.com/t/cursor-3-15-6-silently-disables-all-git-hooks-for-commits-made-from-the-source-control-panel-injects-core-hookspath-dev-null/167914",[70],"nofollow","a second thread",", to a user who had decompiled the bundled extension:",[74,75,76],"blockquote",{},[10,77,78,79,82],{},"The decompilation and your conclusion about the ",[14,80,81],{},"GIT_CONFIG_*"," env vars are spot on, that's exactly what's happening.",[10,84,85,86,91],{},"A second staff member, the same day, in the ",[66,87,90],{"href":88,"rel":89},"https://forum.cursor.com/t/git-hooks-silently-skipped-for-all-source-control-operations-since-3-15-6-git-extension-forces-core-hookspath-dev-null/167719",[70],"main bug thread",":",[74,93,94],{},[10,95,96],{},"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.",[10,98,99,100,102,103,106,107,110,111,114],{},"Worth being precise about intent: ",[14,101,29],{}," is not the only thing in that injected array. Reporters also found ",[14,104,105],{},"safe.bareRepository",", ",[14,108,109],{},"core.fsmonitor"," and ",[14,112,113],{},"core.attributesFile"," alongside it, which reads like a hardening or performance patch that swept up one setting it should not have. Nobody has suggested this was deliberate, and staff said the opposite.",[32,116,118],{"id":117},"we-tested-the-mechanism","We tested the mechanism",[10,120,121,122,124],{},"The forum reports describe the symptom. We wanted to confirm that ",[14,123,29],{}," set to the null device is sufficient on its own, and, more importantly, whether a developer could tell afterwards.",[10,126,127],{},"Git 2.43.0, a fresh repo, and a pre-commit hook that does nothing but refuse:",[129,130,132],"code-block",{"label":131},"The hook",[133,134,139],"pre",{"className":135,"code":137,"language":138},[136],"language-text","#!/bin/sh\necho \"PRE-COMMIT HOOK RAN\"\nexit 1\n","text",[14,140,137],{"__ignoreMap":141},"",[129,143,145],{"label":144},"Three commits, three outcomes",[133,146,149],{"className":147,"code":148,"language":138},[136],"$ git commit -m one\nPRE-COMMIT HOOK RAN\nexit=1\n\n$ git -c core.hooksPath=/dev/null commit -m two\n[master (root-commit) 2c399a8] two\nexit=0\n\n$ GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.hooksPath \\\n  GIT_CONFIG_VALUE_0=/dev/null git commit -m three\n[master 1666cc0] three\nexit=0\n",[14,150,148],{"__ignoreMap":141},[10,152,153],{},"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:",[129,155,157],{"label":156},"What git config says afterwards",[133,158,161],{"className":159,"code":160,"language":138},[136],"$ git config --get core.hooksPath\n$ echo $?\n1\n",[14,162,160],{"__ignoreMap":141},[10,164,165,166,169],{},"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 ",[14,167,168],{},"git config"," will find their setup is fine, because it is.",[171,172,173],"danger-box",{},[10,174,175],{},"This is why it went three weeks without a fix and why it is 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.",[32,177,179],{"id":178},"what-it-turns-off","What it turns off",[10,181,182],{},"Every git hook, and with them everything those hooks invoke. In practice, for this audience, that is:",[184,185,186,202],"table",{},[187,188,189],"thead",{},[190,191,192,196,199],"tr",{},[193,194,195],"th",{},"What you set up",[193,197,198],{},"What it was doing",[193,200,201],{},"Now",[203,204,205,220,230,242,254],"tbody",{},[190,206,207,214,217],{},[208,209,210,213],"td",{},[14,211,212],{},"pre-commit"," running gitleaks or trufflehog",[208,215,216],{},"Blocking a commit that contains an API key",[208,218,219],{},"Does not run",[190,221,222,225,228],{},[208,223,224],{},"Husky-managed chains",[208,226,227],{},"Whatever the chain does, in order",[208,229,219],{},[190,231,232,237,240],{},[208,233,234],{},[14,235,236],{},"lint-staged",[208,238,239],{},"Formatting and linting staged files",[208,241,219],{},[190,243,244,249,252],{},[208,245,246],{},[14,247,248],{},"commit-msg",[208,250,251],{},"Enforcing conventional commit format",[208,253,219],{},[190,255,256,261,264],{},[208,257,258],{},[14,259,260],{},"pre-push",[208,262,263],{},"Last check before code leaves your machine",[208,265,219],{},[10,267,268,269,271],{},"Husky is a hook manager, not a hook. It installs a ",[14,270,212],{}," 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.",[10,273,274],{},"The one a security-minded reader should care about is the first row. A reporter on 26 August named it directly:",[74,276,277],{},[10,278,279],{},"This is especially dangerous because Git hooks may enforce formatting, linting, commit-message validation, secret scanning, or other repository policies.",[10,281,282],{},"Scope that honestly, because the scope matters. The check is bypassed only for commits made through Cursor's Source Control panel, on affected versions. 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.",[10,284,285,286,288],{},"And a local pre-commit hook was never your strongest control anyway. Anyone can pass ",[14,287,16],{}," on any day of the week. But it is the control most commonly recommended to founders shipping AI-built code, it is the one most people believe is watching, and right now, for the click most people use, it is off.",[32,290,292],{"id":291},"check-your-own-setup-in-sixty-seconds","Check your own setup in sixty seconds",[294,295,297,300,306],"step",{"number":296},"1",[10,298,299],{},"In your repo, create a hook that refuses everything:",[133,301,304],{"className":302,"code":303,"language":138},[136],"printf '#!/bin/sh\\nexit 1\\n' > .git/hooks/pre-commit\nchmod +x .git/hooks/pre-commit\n",[14,305,303],{"__ignoreMap":141},[10,307,308,309,312],{},"If you use Husky, point at its directory instead: ",[14,310,311],{},".husky/pre-commit",".",[294,314,316],{"number":315},"2",[10,317,318,319,323],{},"Stage any trivial change, then commit ",[320,321,322],"strong",{},"from the Source Control panel",", the checkmark button in the sidebar.",[294,325,327],{"number":326},"3",[10,328,329],{},"If the commit succeeds, your hooks are being skipped. A working setup refuses the commit with a non-zero exit.",[294,331,333],{"number":332},"4",[10,334,335,336,339],{},"Run the same commit from the integrated terminal with ",[14,337,338],{},"git commit -m test",". It should be blocked. That difference is the bug, and it is also your workaround.",[294,341,343],{"number":342},"5",[10,344,345],{},"Delete the test hook. Then decide what to do about the gap.",[32,347,349],{"id":348},"what-to-do-about-it","What to do about it",[10,351,352,355],{},[320,353,354],{},"Commit from the terminal for now."," This is what Cursor staff recommended on 12 August, and it works because the environment variables are injected into the extension's own child process rather than into your shell.",[10,357,358,361,362,365],{},[320,359,360],{},"Move secret scanning off your laptop."," The durable fix here has nothing to do with Cursor. A control that lives in ",[14,363,364],{},".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:",[367,368,369,376,382],"ul",{},[370,371,372,375],"li",{},[320,373,374],{},"GitHub push protection"," rejects the push itself, server-side. Free on public repositories.",[370,377,378,381],{},[320,379,380],{},"Gitleaks in CI"," runs on the pull request, on GitHub's machines, whatever your editor did.",[370,383,384],{},"Both together, with the pre-commit hook kept as the fast local convenience it always was.",[10,386,387,388,392],{},"Our ",[66,389,391],{"href":390},"/blog/how-to/secret-scanning","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.",[10,394,395,398,399,403],{},[320,396,397],{},"Check what already shipped."," If you have been committing through the panel since early August and you relied on that hook, the useful question is not whether the hook will run tomorrow. It is whether anything got through in the meantime. ",[66,400,402],{"href":401},"/blog/how-to/detect-secrets-in-git","Scanning git history for secrets"," is a different job from blocking new ones, and this is the situation it exists for.",[405,406,407],"warning-box",{},[10,408,409],{},"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.",[32,411,413],{"id":412},"is-this-a-vs-code-bug","Is this a VS Code bug?",[10,415,416],{},"No, and it is worth being clear about that since Cursor is a VS Code fork.",[10,418,419,420,423,424,427,428,106,431,110,434,437,438,441],{},"We checked the upstream source. The git extension in ",[14,421,422],{},"microsoft/vscode"," sets exactly one environment variable, ",[14,425,426],{},"GIT_OPTIONAL_LOCKS=0",". The strings ",[14,429,430],{},"hooksPath",[14,432,433],{},"GIT_CONFIG_KEY",[14,435,436],{},"devNull"," do not appear in ",[14,439,440],{},"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.",[32,443,445],{"id":444},"where-it-stands","Where it stands",[10,447,448],{},"The main thread was last active on 26 August 2026. It is still open, has no accepted solution, and has had no staff reply since 12 August. Users have reported the bug persisting in 3.16.29 (20 August), 3.17.8 (23 August) and 3.17.21 (25 August), that last one on the day the build shipped.",[10,450,451],{},"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.",[453,454,455],"info-box",{},[10,456,457,458,462],{},"This post describes the state of an open bug as of 2026-08-27. Check the ",[66,459,461],{"href":88,"rel":460},[70],"forum thread"," before assuming it still applies. A fix would land in a Cursor release rather than in anything you configure.",[464,465,466,476,485,491,504],"faq-section",{},[467,468,470],"faq-item",{"question":469},"Why is my pre-commit hook not running in Cursor?",[10,471,472,473,475],{},"Since Cursor 3.15.6, commits made through the Source Control panel run with ",[14,474,29],{}," pointed at the platform's null device, so git finds no hooks to run. Cursor staff confirmed on 2026-08-10 that this is not intended. Committing the same staged changes from the integrated terminal still runs hooks.",[467,477,479],{"question":478},"Is Husky broken in Cursor?",[10,480,481,482,484],{},"Husky itself is fine. It installs a ",[14,483,212],{}," 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.",[467,486,488],{"question":487},"Which Cursor versions are affected?",[10,489,490],{},"It starts at 3.15.6, which reached the stable channel in the first week of August 2026. Users have since reported it still present in 3.16.29, 3.17.8 and 3.17.21, the last of those on the day that build shipped. No fix has been announced as of 2026-08-27.",[467,492,494],{"question":493},"How do I check whether my hooks are running?",[10,495,496,497,499,500,503],{},"Add a ",[14,498,212],{}," hook containing only ",[14,501,502],{},"exit 1",", stage a change, and commit from the Source Control panel. If the commit succeeds, hooks are being skipped. Then run the same commit from the terminal and watch it get blocked. Delete the hook afterwards.",[467,505,507],{"question":506},"Does this affect VS Code?",[10,508,509,510,512,513,427,516,106,518,110,520,522],{},"No. The git extension in ",[14,511,422],{}," sets one environment variable, ",[14,514,515],{},"GIT_OPTIONAL_LOCKS",[14,517,430],{},[14,519,433],{},[14,521,436],{}," do not appear in that extension's source at all, so this is a change in Cursor's fork rather than upstream behaviour.",[524,525,526,531,535],"related-articles",{},[527,528],"related-card",{"description":529,"href":390,"title":530},"Gitleaks, GitHub push protection and CI scanning, which is where this check belongs once a local hook can be turned off without telling you.","Scan a Git Repo for Secrets",[527,532],{"description":533,"href":401,"title":534},"Finding keys that made it into history, which is the question to ask after three weeks of hooks not firing.","Detect Secrets Already in Git",[527,536],{"description":537,"href":538,"title":539},"The wider set of Cursor defaults worth checking, from generated code patterns to what the agent can reach.","/blog/best-practices/cursor","Cursor Security Best Practices",[541,542,545,549],"cta-box",{"href":543,"label":544},"/","Start Free Scan",[32,546,548],{"id":547},"did-anything-slip-through","Did Anything Slip Through?",[10,550,551],{},"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.",{"title":141,"searchDepth":553,"depth":553,"links":554},2,[555,556,557,558,559,560,561,562],{"id":34,"depth":553,"text":35},{"id":117,"depth":553,"text":118},{"id":178,"depth":553,"text":179},{"id":291,"depth":553,"text":292},{"id":348,"depth":553,"text":349},{"id":412,"depth":553,"text":413},{"id":444,"depth":553,"text":445},{"id":547,"depth":553,"text":548},"how-to","2026-08-27","Cursor's Source Control panel has skipped every git hook since 3.15.6. Staff confirmed it on 10 August, it is still there, and your gitleaks hook is off.",false,"md",[569,571,573,574,576],{"question":469,"answer":570},"Since Cursor 3.15.6, 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 staff confirmed on 2026-08-10 that this is not intended. Committing the same staged changes from the integrated terminal still runs hooks.",{"question":478,"answer":572},"Husky itself is fine. It installs a pre-commit hook, and that hook is what gets skipped. The same applies to anything a hook invokes, including lint-staged and gitleaks. Nothing in your Husky config needs changing.",{"question":487,"answer":490},{"question":493,"answer":575},"Add a pre-commit hook containing only 'exit 1', stage a change, and commit from the Source Control panel. If the commit succeeds, hooks are being skipped. Then run the same commit from the terminal and watch it get blocked. Delete the hook afterwards.",{"question":506,"answer":577},"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.","yellow",null,"cursor pre-commit hook not running, cursor git hooks skipped, cursor husky not working, core.hooksPath dev null, cursor source control commit, gitleaks pre-commit not running",{"trendTrigger":582,"trendDate":583},"cursor-source-control-injects-core-hookspath-devnull-since-3.15.6","2026-08-07",true,"Your pre-commit hook does not run when you commit from Cursor's Source Control panel. No error, and nothing in git config to show it happened.","/blog/how-to/cursor-pre-commit-hooks-not-running","8 min read","[object Object]","HowTo",{"title":5,"description":565},{"loc":586},"blog/how-to/cursor-pre-commit-hooks-not-running",[],"summary_large_image","aPR8tulfTcxkGfuU4mna6VfF2tuFzHHaIPXwMhoIvc8",1789672859767]