Git hooks silently skipped for all Source Control operations since 3.15.6 — git extension forces `core.hooksPath=/dev/null`

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

As of Cursor 3.15.6, committing from the Source Control panel silently skips all git hooks. pre-commit, commit-msg, Husky, and anything else in .git/hooks never runs. There is no error, no warning, and no --no-verify on the command line — the commit just succeeds as if the hooks did not exist. Committing the same staged changes from the integrated terminal runs the hooks correctly.
This is a serious silent-failure: linting, formatting, generated-file checks, and secret scanning are all bypassed, and the user has no indication anything was skipped. In my case a code-generation hook that keeps generated files in sync with a source CSV was bypassed, and I committed an inconsistent tree several times before noticing.
Root cause: the bundled git extension injects a hardcoded set of config overrides into the environment of every git process it spawns. In /Applications/Cursor.app/Contents/Resources/app/extensions/git/dist/main.js:

var La = [
  ["safe.bareRepository", "explicit"],
  ["core.fsmonitor", "false"],
  ["core.hooksPath", Jo.devNull],       // os.devNull → /dev/null
  ["core.attributesFile", Jo.devNull]
];

These are appended as GIT_CONFIG_KEY_n / GIT_CONFIG_VALUE_n pairs (with GIT_CONFIG_COUNT bumped) by the env-building function, which also overwrites any of these keys the caller had already set. Because core.hooksPath points at /dev/null, git finds no hooks.
Applying these overrides to background/read-only operations is reasonable, but they are also applied to commit, which defeats hooks entirely.
None of these four keys exist anywhere in upstream microsoft/vscode’s git extension (extensions/git/src/git.ts, main.ts, util.ts), so this is Cursor-specific.
This is also invisible to normal debugging: because the override arrives via the environment rather than a config file, git config --show-origin --get-all core.hooksPath reports nothing, and .git/hooks/pre-commit is present and executable.

Steps to Reproduce

  1. In any repo, install a hook that always fails: printf '#!/bin/sh\nexit 1\n' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
  2. Stage any change.
  3. Commit from the Source Control panel. The commit succeeds.
  4. Undo it (git reset --soft HEAD~) and run git commit -m test in the integrated terminal. The commit is correctly blocked.
    To confirm the mechanism, this reproduces the exact behaviour from a shell:
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.hooksPath GIT_CONFIG_VALUE_0=/dev/null \
  git -c user.useConfigOnly=true commit --quiet --allow-empty-message --file -
Logs

The Git output channel shows no --no-verify, and the timing makes the regression obvious. From my own Git.log history in the same repo with the same hooks:

2026-07-17 16:48:51  > git ... commit --quiet --allow-empty-message --file -  [1108ms]
2026-07-20 13:11:39  > git ... commit --quiet --allow-empty-message --file -  [1690ms]
2026-07-26 13:00:16  > git ... commit --quiet --allow-empty-message --file -  [2130ms]
2026-08-05 13:27:40  > git ... commit --quiet --allow-empty-message --file -  [1286ms]
2026-08-06 16:36:44  > git ... commit --quiet --allow-empty-message --file -  [1398ms]   <-- last good
2026-08-07 10:33:17  > git ... commit --quiet --allow-empty-message --file -  [69ms]     <-- first bad
2026-08-07 13:44:03  > git ... commit --quiet --allow-empty-message --file -  [74ms]
2026-08-07 13:57:53  > git ... commit --quiet --allow-empty-message --file -  [86ms]

Every commit from July 17 to Aug 6 took 800–2100 ms (hooks running). Every commit from Aug 7 onward takes 62–86 ms (hooks skipped). The identical command run from a terminal takes ~955 ms and correctly fails on the hook.
The 3.15.6 bundle was written to disk on Aug 6 at 02:14, but my Cursor session had been running since Jul 27, so the old build stayed active all through Aug 6. The log session directory 20260807T095326 shows Cursor restarted at 09:53 on Aug 7 — and the very next commit, at 10:33, was the first to skip hooks.

Expected Behavior

Commits made from the Source Control panel should run git hooks, exactly as git commit does from a terminal, unless the user explicitly chooses a “Commit (No Verify)” command. If hooks must be suppressed for the extension’s internal/background git calls, the override should be scoped to those calls and not applied to commit, merge, rebase, or push.

Operating System

MacOS

Version Information

Version: 3.15.6
Commit: a1f686545fd0ce8917bbd2449f733551a9bce420
Date: 2026-08-06T01:41:03.876Z
VS Code Version: 1.128.0

Also: git 2.50.1 (Apple Git-155), pre-commit 4.5.0. Not Remote SSH or WSL — plain local workspace.

Does this stop you from using Cursor

Sometimes - I can sometimes use Cursor

Experiencing same

Same ignored behavior observed also.

The same issue. And cursor gives the reason:

Root Cause, High Confidence

The Cursor built-in Git extension (extensions/git/dist/main.js) injects the following Git configuration through GIT_CONFIG_* environment variables whenever it spawns a Git subprocess:

["core.hooksPath", os.devNull]

On macOS, os.devNull resolves to /dev/null.

As a result, the effective configuration for Git operations initiated from Cursor Source Control becomes:

core.hooksPath = /dev/null

Git then looks for hooks under /dev/null. Since no hook files can exist there, Git silently skips all hooks and proceeds with the commit.

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. I’ll post here when there’s an update.

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

From the Source control tab, clicking the commit button is no longer running any configured precommit hooks.

Steps to Reproduce

Click Commit button in source control tab

Expected Behavior

configured precommit hooks to still run

Operating System

MacOS

Version Information

Version: 3.15.6 (Universal)
VS Code Extension API: 1.128.0
Commit: a1f686545fd0ce8917bbd2449f733551a9bce420
Date: 2026-08-06T01:41:03.876Z
Layout: IDE
Build Type: Stable
Release Track: Default
Electron: 40.10.3
Chromium: 144.0.7559.236
Node.js: 24.15.0
V8: 14.4.258.32-electron.0
xterm.js: 6.1.0-beta.291
OS: Darwin arm64 25.4.0

Does this stop you from using Cursor

No - Cursor works, but with this issue

**Additional confirmation from Windows, plus a way to make git itself report the override**

Same root cause as above, independently reproduced on Windows. Adding it since both existing
reports are macOS/source-analysis based, and because there's a diagnostic here that lets anyone
verify this from inside Cursor's own git process — useful given the override travels in the
environment and so never shows up in the command Cursor logs.

Environment:
- Cursor 3.15.6 — `resources/app/extensions/git/dist/main.js`, 453,563 bytes, dated Aug 6 02:22
- git 2.47.1.windows.1, Windows 10
- husky 9.1.7, repo has `core.hooksPath=.husky/_`

On Windows the injected value is `\\.\nul` (`os.devNull`), so the effect is identical.

**Making git state it directly.** trace2 can be enabled through config rather than environment,
which is what makes it usable here:

    git config --global trace2.normalTarget C:/path/to/trace2.log
    git config --global trace2.configParams core.hooksPath

Set it globally, not repo-locally — trace2 initialises before the repository config is read.
Then commit once from the Source Control panel. The commit process logs:

    14:32:26.583799 common-main.c:58    start git.exe -c user.useConfigOnly=true commit --quiet --allow-empty-message --file -
    14:32:26.594799 repository.c:241    worktree C:/src/example-repo
    14:32:26.595298 git.c:480           cmd_name commit (commit)
    14:32:26.595298 trace2.c:437        def_param scope:local   core.hookspath=.husky/_
    14:32:26.595298 trace2.c:437        def_param scope:command core.hookspath=\\.\nul
    14:32:26.622300 run-command.c:734   child_start[0] git maintenance run --auto --quiet --detach

Two things worth noting. Git reads the repository's `core.hooksPath` and then has it overridden at
**command** scope, which outranks every config file — so this is not something a user can undo from
`.git/config`, a global config, or `GIT_CONFIG_*`. And the only child process spawned by the commit
is `git maintenance`; there is no hook child at all.

The same commit from a terminal, for contrast:

    14:30:34.906132 trace2.c:437        def_param scope:local core.hookspath=.husky/_
    14:30:34.914631 run-command.c:734   child_start[0] .husky/_/pre-commit

Timings match the other reports: 57 ms and 67 ms for Source Control commits, versus 38–40 s from a
terminal where the hook actually runs.

**One extra data point for anyone debugging husky specifically.** I replaced `.husky/_/pre-commit`
with a wrapper that logs and then delegates to husky's `h` as normal. On Source Control commits it
never logged anything, while terminal commits (both Git Bash and native PowerShell) logged every
time. So git never executes the hook file — husky is not bailing out early, and this is not the
usual `HUSKY=0` / cwd / PATH / executability class of husky problem. Ruling that out took a while,
so it may save someone else the detour.

**Baseline comparison against a newer VS Code than previously cited.** In stock VS Code 1.132.0
(git extension bundle dated Aug 4, 757,288 bytes), all six markers are absent — `hooksPath`,
`safe.bareRepository`, `core.fsmonitor`, `attributesFile`, `GIT_CONFIG_KEY`, `devNull` — each
`grep -c` returns 0, versus 1 for each in Cursor's bundle. Upstream has none of this machinery.

Suppressing `core.fsmonitor` and `core.attributesFile` for the extension's own background reads
seems reasonable; `core.hooksPath` appears to have been swept into the same list, and it applies to
user-initiated commits too.

To undo the tracing afterwards:

    git config --global --unset-all trace2.normalTarget
    git config --global --unset-all trace2.configParams

Same issue on macOS with Husky + lint-staged on Cursor 3.15.6.

Source Control commits silently skip hooks; terminal commits work. VS Code is fine.

Downgraded to 3.14.27 — SCM commits + Husky work again.

Experiencing same issue. It’s frustrating. Please look into this.

Thanks for the Windows confirmation and trace2 writeup, @MaksymV - matches what we’re seeing.

For anyone hitting this: it’s not platform-specific, and committing the same staged changes from the integrated terminal still runs your hooks in the meantime. We’ve let the team know and this is an issue we’re tracking - I’ll post here as soon as there’s an update.

Look forward to the fix. 3.16.29 is still bugged.

Same here: no pre-commit - no waiting for checks :grinning_face:

3.17.8 still buggy as well

Is this still being looked at? Not being able to commit without hooks running is… A pretty huge deal.

3.17.21, issue still there

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Commits created from Cursor’s Source Control UI silently bypass all repository Git hooks.

The commit command shown in the Git output does not contain --no- verify:

  git -c user.useConfigOnly=true commit --quiet --allow-empty-message
  --file -

However, Cursor’s installed Git extension injects command-scoped Git
configuration that overrides:

  core.hooksPath=/dev/null

This takes precedence over the repository’s configured hook path and
causes Git
to ignore pre-commit, commit-msg, and other hooks.

In my repository:

  git config --show-origin --get-all core.hooksPath

returns:

  file:.git/config    .husky/_

The Husky hooks and wrappers exist and are executable. There is no
HUSKY=0,
no --no-verify, and no user-level Husky configuration disabling
hooks.

The repository’s pre-commit hook runs:

  pnpm --silent format:staged

The original Cursor commit completed in 32 ms and contained code that
Prettier
reports as unformatted. The hook was therefore not executed.

This is especially dangerous because Git hooks may enforce formatting,
linting, commit-message validation, secret scanning, or other repository
policies. The commit succeeds without any indication that those checks were skipped.

Steps to Reproduce

  1. Open a Git repository in Cursor under WSL2.

  2. Configure an executable Git pre-commit hook that always fails, for
    example:

    #!/bin/sh
    echo "pre-commit hook executed" >&2
    exit 1
    
  3. Verify the hook path:

    git config --get core.hooksPath
    
  4. Stage a change.

  5. Commit using Cursor’s Source Control UI.

  6. Observe that the commit succeeds and the hook output is absent.

  7. Run the equivalent commit from an integrated terminal.

  8. Observe whether the hook executes and blocks the commit.

Expected Behavior

An explicit user-initiated commit from the Source Control UI should
execute the
repository’s Git hooks.

If Cursor cannot execute hooks safely while extension isolation is
active, it
should do one of the following instead of silently bypassing them:

  • ask the user whether trusted repository hooks may run;
  • provide a documented setting for user-initiated commits;
  • block the commit and explain why hooks cannot run; or
  • clearly warn that all hooks are being skipped.

Agent-initiated Git operations may require stronger isolation, but an
explicit
Source Control UI commit should not silently behave like --no-verify.

Operating System

Linux

Version Information

Cursor version

Version: 3.15.6
Commit: a1f686545fd0ce8917bbd2449f733551a9bce420
Date: 2026-08-06T01:41:03.876Z
Build: Stable

Environment

OS: WSL2 Linux
Kernel: 6.18.33.2-microsoft-standard-WSL2
Architecture: x86_64
Git: 2.40.1
Node: 22.23.1
Hook manager: Husky 9.1.7

Additional Information

The installed Cursor Git extension contains logic that adds command-
scoped Git
configuration resembling:

  [
    ["safe.bareRepository", "explicit"],
    ["core.fsmonitor", "false"],
    ["core.hooksPath", os.devNull],
    ["core.attributesFile", os.devNull]
  ]

This appears to happen when Cursor extension isolation is enabled.

This may be related to Cursor’s previous Git-hook sandbox security
mitigation,
but user-initiated Source Control commits currently receive no warning
that
repository hooks are being disabled

Does this stop you from using Cursor

No - Cursor works, but with this issue

Any updates yet ?

It’s been 3 weeks

Bruh, need this fixed

@mohitjain may I get an update on this?

Hi @Red_Spoke, I understand the frustration here.

If you’re committing through Cursor’s Source Control panel, this matches a known issue we’re tracking. Commits started there can currently skip Git hooks, including pre-commit, commit-msg, and Husky hooks. This isn’t caused by your repository setup.

For now, commit from Cursor’s integrated terminal using git commit; hooks should run normally there.

I’m merging this into the main thread so updates stay in one place.