Private team marketplace plugin install fails on 3.2.11 -- three-stage clone fallback all broken

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

When Cursor tries to install/refresh a plugin from a private GitHub team marketplace, it walks a three-stage clone fallback. In 3.2.11, all three stages are broken, so any user without a pre-existing local clone is silently locked out:

  1. Stage 1 – embedded SSH clone: fails for any user without ~/.ssh/id_ed25519.
  2. Stage 2 – embedded HTTPS clone via staging directory: fails because Cursor’s embedded git library exposes no working auth (no Windows Credential Manager / Keychain backend, no Cursor-side GitHub OAuth, no _netrc, no embedded URL credentials reading from anywhere usable).
  3. Stage 3 – System git fetch direct install: hard-coded with -c credential.helper= -c credential.interactive=false -c core.askPass=, which actively disables every working auth mechanism on the user’s system, including any valid GCM token already in the Windows Credential Manager.

To make it worse, the plugin loader logs 2 plugins loaded, 1 failures and the IDE UI shows the plugin as “loaded” – users do not realize their plugin is non-functional until skills/commands silently disappear.

Severity: High – silently breaks plugin distribution for any private team marketplace; only worked for users who happened to have an old cached clone from a previous Cursor version.

Steps to Reproduce

  1. Have a private GitHub repository containing a Cursor plugin (.cursor-plugin/plugin.json etc.).
  2. Register it as a Team Marketplace in Cursor Dashboard.
  3. On a fresh user account (no SSH keys, no _netrc, no embedded GitHub credentials in ~/.gitconfig), install Cursor.
  4. Sign into Cursor.
  5. Cursor attempts auto-install of the team plugin → fails silently.

To reproduce on a previously-working machine:

  1. Delete ~\.cursor\plugins\marketplaces\github.com\<org>\<repo>\<commit>\.
  2. Delete ~\.cursor\plugins\cache\<marketplace>\<plugin>\<commit>\.
  3. Ctrl+Shift+PDeveloper: Reload Window.
  4. Observe failure in ~\AppData\Roaming\Cursor\logs\<timestamp>\window1\exthost\anysphere.cursor-agent-exec\Cursor Plugins.log.

Expected Behavior

Either:

  • (a) Auto-install succeeds using credentials the user already has on the system (working GCM token in Windows Credential Manager, an _netrc entry, an SSH key, or Cursor’s own Dashboard GitHub OAuth integration).
  • (b) A clear, actionable, user-facing error: “Cannot authenticate to – please run git clone <url> once manually so credentials get cached, or follow .”

Operating System

Windows 10/11

Version Information

Version: 3.2.11 (Stable, Windows x64)
Commit: e9ee1339915a927dfb2df4a836dd9c8337e17cc0
Git for Windows: 2.53.0
GCM (Git Credential Manager): installed via system config (credential.helper=manager)
GitHub tenant: GitHub Enterprise Cloud with EMU

Additional Info:
SSO: GitHub auth delegated to Microsoft Entra ID / M365 (no separate GitHub password — login flow goes through M365)

Additional Information

undefined## Actual Behavior – Cursor Plugins log

[info] BackendMarketplaceClient: Adding enabled plugin: <plugin> from <commit> at plugins/<plugin>
[info] loadFromMarketplaceSource: Plugin missing from cache, installing: <plugin>
[info] MarketplaceCacheManager: Cloning into staging directory: .../marketplaces/_staging/<uuid>
[error] Falling back to HTTPS clone due to SSH clone failure
[error] Failed to clone marketplace repository via staging clone
[warn]  Failed to install plugin <plugin> from marketplace cache, falling back to direct install
[error] Cursor plugin load error <plugin>@<commit>: Command failed:
        git -c credential.helper= -c credential.interactive=false -c core.askPass= fetch --depth 1 origin <commit>
        fatal: unable to get password from user
[info]  Plugins reload completed: 2 plugins loaded (0 extension), 1 failures

Root Cause Analysis

Stage 1 – embedded SSH clone

Falling back to HTTPS clone due to SSH clone failure. The user has no ~/.ssh/id_ed25519. No public documentation states an SSH key is required for Team Marketplace. Many Windows users authenticate via GCM/HTTPS only.

Stage 2 – embedded HTTPS clone (libgit2 / nodegit) via staging directory

Failed to clone marketplace repository via staging clone. Cursor’s embedded git library appears to attempt no credential lookup whatsoever. It does not consult:

  • Windows Credential Manager (no wincred backend exposed). cmdkey /list showing a valid git:https://github.com entry is ignored.
  • _netrc / .netrc (the embedded library skips this even though curl reads it natively in stage 3).
  • The Cursor IDE’s GitHub OAuth integration (Dashboard → Integrations → GitHub) – that token is only used for Cloud Agents / Bugbot.
  • The VS Code vscode.github-authentication extension’s session store.
  • gh CLI’s stored OAuth token.

Stage 3 – System git fetch direct install

git -c credential.helper= -c credential.interactive=false -c core.askPass= fetch --depth 1 origin <commit>

fatal: unable to get password from user. Three problems compounding:

  • -c credential.helper= – disables every credential helper (GCM, wincred, manager, store).
  • -c credential.interactive=false – forbids interactive credential prompts (blocks the Browser-OAuth popup that worked in earlier Cursor versions).
  • -c core.askPass= – disables askpass fallback.

Reproduced via PowerShell with GIT_TRACE=1 GIT_CURL_VERBOSE=1:

== Info: Could not find host github.com in the .netrc file; using defaults
=> Send header: GET /<org>/<repo>/info/refs?service=git-upload-pack HTTP/1.1
<= Recv header: HTTP/1.1 401 Unauthorized
<= Recv header: www-authenticate: Basic realm="GitHub" enterprise_hint="<tenant>" domain_hint="<short>"
fatal: unable to get password from user

GitHub returned a clean Basic auth challenge. Git could not produce a credential because Cursor disabled every channel.

Misleading Telemetry / UX

After all three stages fail:

loadFromMarketplaceSource completed in 3538.9ms (2 plugins loaded, 1 failures)
Plugins reload completed: 2 plugins loaded (0 extension), 1 failures

The plugin is reported as one of “2 plugins loaded” with 1 failures buried in metadata. The IDE UI shows the plugin as “loaded” (with skills/commands gone). Users only realize the plugin is broken when commands silently disappear.

Why this hits a lot of users

In earlier Cursor versions, an interactive Browser-OAuth popup appeared on first plugin install, captured a token, and stored it via GCM. Once stages 1 and 2 were added in 2.6/3.x and stage 3 was hard-coded to ignore helpers, every fresh user is locked out – but anyone who installed Cursor before this change still has a cached clone, so they don’t notice.

Related forum threads:

Workaround that works today

The only auth mechanism that survives all three stages is _netrc, because curl (used internally by Git’s HTTPS transport in stage 3) reads it natively, before any credential helper runs. None of Cursor’s -c flags affect it.

Per-user OAuth (recommended for SSO-managed orgs)

winget install GitHub.cli
gh auth login --hostname github.com --git-protocol https --web
$token = gh auth token
@"
machine github.com
login x-access-token
password $token
"@ | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII -Force

The browser flow uses the user’s existing SSO session, so it’s a single-click confirmation. Resulting token is user-specific (good for audit/revocation), short-lived, refreshable via gh auth refresh.

Centralized bot PAT (no user interaction)

@"
machine github.com
login x-access-token
password <BOT_PAT>
"@ | Out-File "$env:USERPROFILE\_netrc" -Encoding ASCII -Force

Fine-grained, scoped to read-only on the marketplace repo only. Distribute via Intune/MDM. Token rotation done centrally.

Verified working

PS> git -c credential.helper= -c credential.interactive=false -c core.askPass= fetch --depth 1 origin <commit>
* branch            <commit> -> FETCH_HEAD
PS> $LASTEXITCODE
0

After the _netrc was in place, Cursor’s auto-install completed successfully on Developer: Reload Window.

Suggested Fix (in priority order)

  1. Stage 2 (embedded HTTPS) should consult OS credential storage. On Windows query Windows Credential Manager for git:https://<host>. On macOS query Keychain. On Linux honor libsecret. Restores the “if git clone works in your terminal, plugin install also works” UX.
  2. Pipe the IDE’s GitHub OAuth integration to the plugin loader. That token is already there for Cloud Agents/Bugbot – surface it to stage 1/2 for matching repos.
  3. Stop hard-coding -c credential.helper= -c credential.interactive=false -c core.askPass= in stage 3. Either drop these flags entirely or expose a setting. To prevent hung sessions, use GIT_TERMINAL_PROMPT=0 plus a process timeout instead.
  4. Restore (or document the disappearance of) the interactive Browser-OAuth popup that earlier Cursor versions showed on first private plugin install.
  5. Surface plugin load failures in the IDE UI. A toast notification with a link to the log + a “Retry” button would solve most user confusion.
  6. Update the Cursor Plugins documentation page (cursor.com/docs/plugins) to state explicitly which auth mechanisms are supported for private team marketplaces.

Reproduction artifacts I can share

  • Full Cursor Plugins.log excerpts
  • GIT_TRACE=1 + GIT_CURL_VERBOSE=1 output of stage 3
  • PowerShell trace showing no git.exe is invoked during stage 1/2
  • Filesystem snapshots of ~\.cursor\plugins\marketplaces\_staging\

Happy to provide any of these on request.

Does this stop you from using Cursor

No - Cursor works, but with this issue

Addendum — Code-level evidence from 3.2.11

I went one layer deeper and traced the actual loader code in
resources/app/out/vs/workbench/api/node/extensionHostProcess.js
(Cursor 3.2.11, build e9ee1339915a927dfb2df4a836dd9c8337e17cc0).
A couple of points from my original write-up turn out to need correcting,
and one piece of feedback (“restore the Browser-OAuth popup”) turns out
to be a feature request rather than re-enabling a hidden switch.

1. Stage 2 is not a libgit2/nodegit path. It is the same git subprocess as Stage 3.

The MarketplaceCacheManager exposes two clone strategies:

cloneDirect(...)     → cloneResolvedRef(...) → wu() → child_process.execFile("git", [...IVs(), ...args], JVs(...))
cloneViaStaging(...) → cloneResolvedRef(...) → wu() → child_process.execFile("git", [...IVs(), ...args], JVs(...))

Both strategies funnel into the same helper wu(). The only difference
between “stage 2” (cloneViaStaging) and “stage 3” (cloneDirect) is
the target directory (a UUID staging dir under marketplaces/_staging/
vs. the canonical cache dir) — auth handling is identical. So my
earlier hypothesis that stage 2 was an embedded libgit2 path with its
own credential lookup was wrong; there is no embedded HTTPS path at all.
That actually makes the auth situation simpler to describe: there is
exactly one auth path, and it is the muzzled one.

2. The hard-coded muzzle is a 6-channel knockout, not just credential.interactive=false.

function IVs() {
  return ["-c", "credential.helper=",
          "-c", "credential.interactive=false",
          "-c", "core.askPass="];
}

function JVs(s) {
  const e = {
    ...process.env,
    GIT_TERMINAL_PROMPT: "0",
    GIT_CONFIG_NOSYSTEM:  "1",     // ignores system gitconfig (helper=manager included)
    GIT_ASKPASS:                  undefined,
    VSCODE_GIT_ASKPASS_NODE:      undefined,
    VSCODE_GIT_ASKPASS_MAIN:      undefined,
    VSCODE_GIT_ASKPASS_EXTRA_ARGS:undefined,
    GCM_INTERACTIVE: "Never",
    GCM_PROVIDER:    "none",
  };
  ...
}

IVs() is a parameterless constant function. It reads no setting, no
workspace state, no env var. There is therefore no toggle — neither
in settings.json, nor via env, nor via gitconfig — that flips
credential.interactive=false to true. In addition to the three -c
flags, the spawn env also unsets GIT_ASKPASS / VSCODE_GIT_ASKPASS_*
and disables GCM with GCM_INTERACTIVE=Never + GCM_PROVIDER=none,
plus turns off the system-level gitconfig with GIT_CONFIG_NOSYSTEM=1
(so a globally configured credential.helper=manager is ignored too).

This explains why, in my testing, even fully-working setups
(cmdkey /list shows a valid git:https://github.com GCM entry,
git clone works in any normal terminal) still fail: Cursor disables
the helper that holds that token, and disables the interactive fallback
that would acquire one.

3. The plugin loader does not use vscode.github-authentication.

The string vscode.github-authentication appears exactly once in the
~5.1 MB bundle, and only in a list of bundled built-in extensions
(alongside anysphere.cursor-deeplink, anysphere.cursor-resolver-helper,
anysphere.cursor-socket). It is loaded as an extension; it is not
called by the marketplace clone code. There is no
getSession(..., { createIfNone: true }) invocation anywhere on the
clone path. So the “Reading sessions from keychain…” log line
mentioned earlier is from the unrelated extension activation, not from
the loader.

4. So where did the old popup come from?

Three plausible sources, only one of which is plausibly historical:

Source Status today
Git Credential Manager (system) opening its own browser actively suppressed by credential.helper= + GCM_INTERACTIVE=Never + GCM_PROVIDER=none
Built-in vscode.github-authentication getSession({createIfNone:true}) not called by the plugin loader
Cursor Dashboard GitHub integration token (Cloud Agents/Bugbot) not piped to the plugin loader

The popup users remember was almost certainly the system GCM browser
flow
. In a Cursor version that did not yet pass IVs()/JVs(),
calling git clone against a private GitHub URL would naturally trigger
GCM, which opens the OAuth browser flow and stores a token in Windows
Credential Manager. From that point on, every subsequent invocation
finds a cached token. Users who experienced this once still have a
working git:https://github.com Credential Manager entry today —
they just don’t benefit from it because Cursor’s current code
explicitly bypasses the helper that holds it.

So this isn’t a hidden-switch problem; the popup was an emergent side
effect of GCM not being suppressed, and that suppression is now
hard-coded.

Suggested fix (refined)

I’d narrow my original suggestion #3 to the specific, minimal-impact
form:

In IVs() / JVs(), drop -c credential.helper=,
-c credential.interactive=false, -c core.askPass=,
GIT_CONFIG_NOSYSTEM=1, GCM_INTERACTIVE=Never, GCM_PROVIDER=none,
and the GIT_ASKPASS / VSCODE_GIT_ASKPASS_* resets.
Keep GIT_TERMINAL_PROMPT=0 plus a process timeout if the goal is
avoiding hung sessions on bad credentials.

That single change would let any of the four user-side mechanisms
(GCM token in Windows Credential Manager, macOS Keychain, libsecret,
_netrc) work, without Cursor having to grow its own credential
backend. It also restores the historical “first install pops up the
browser via GCM” UX as a free byproduct, with no new code.

I can share the relevant minified function bodies on request.

+1 this is a pretty major bug as its preventing our Team marketplace from working properly.

Hey, thanks for the report. We rarely see this level of detail, and the code-level breakdown of IVs() and JVs() plus the trace through all three stages really helped. Repro matches what we see on our side. On Windows, the combo of -c credential.helper= + credential.interactive=false + core.askPass= + GCM_INTERACTIVE=Never + GCM_PROVIDER=none + GIT_CONFIG_NOSYSTEM=1 really disables every working auth channel for private repos.

We’ve filed this internally and it’s being tracked. I can’t share an ETA yet, but the direction is clear. We either need to re-enable credential helpers and askpass for the plugin clone path, or pass the Cursor GitHub OAuth session into the loader. The silent “loaded with 1 failure” UI message is also on our radar. Good call, it’s misleading.

For a workaround, _netrc with gh auth token or a fine-grained PAT via MDM for a centralized setup is currently the only working option. curl reads netrc natively before credential helpers, and our -c flags don’t affect it. Good stop-gap for the team.

I’ll also pass along the request to document supported auth mechanisms for team marketplaces. It’s not clearly documented today, and that’s part of the problem.

@kbrouder, the same workaround should work for you too until the fix lands.

If we have an update, we’ll reply in the thread.

Thanks for the update, good to know it’s tracked.

For the record, on Windows I ended up with a working SSH-only setup instead of netrc/PAT. Wrote it up as an agent-runnable guide.

What threw me off: the very first clone attempt Cursor makes is already SSH ([email protected]:...), so I assumed a standard id_ed25519 + key on GitHub would just work. It didn’t, and the log line Falling back to HTTPS clone due to SSH clone failure is the only hint — no reason given. Two non-obvious things were needed:

  1. A dedicated key (id_ed25519_cursor) plus an explicit Host github.com block in ~/.ssh/config with IdentityFile + IdentitiesOnly yes. Without that, SSH silently offers the default key and GitHub answers Permission denied (publickey).
  2. Configure SSO → Authorize on the key for the 10-DevLab org. Easy to miss because ssh -T [email protected] still says “Hi <user>! …” without it — the SAML error only shows up on the actual clone.

Once both are in place, the exact Cursor invocation (git -c credential.helper= -c credential.interactive=false -c core.askPass= clone --depth 1 ...) succeeds, and the plugin loads without the HTTPS fallback.

So as a second workaround alongside _netrc + PAT: SSH key + ~/.ssh/config + SSO authorize works today, no PAT rotation needed. Might be worth mentioning in the docs you’re planning around supported auth mechanisms.

Thanks for coming back with the SSH option. Two things you pointed out are genuinely non-obvious:

  1. A dedicated key plus an explicit Host github.com block with IdentityFile and IdentitiesOnly yes. Without this, SSH quietly offers the default key and GitHub replies with Permission denied (publickey).
  2. SSO authorization for the key for the org. ssh -T [email protected] works even without it, so it’s easy to miss. The SAML error only shows up during the actual clone.

Also a fair callout on the log message Falling back to HTTPS clone due to SSH clone failure. Without the reason, it’s not very helpful.

I’ve passed both points to the folks updating the docs on supported auth mechanisms for team marketplaces. On the actual fix so GCM/Keychain/libsecret work again without workarounds, I can’t share an ETA yet. The issue is being tracked, and I’ll post an update in the thread as soon as I have one.