Skip to content

Cursor skill discovery drops all workspace skills when any user-level skills folder exceeds the scan depth limit #17215

Description

@ethan-lipseys

What happened

When using models through the Cursor provider, neither / slash commands nor $ skill mentions show any of the skills in my project. The project has 28 skills under .agents/skills and .claude/skills, and all of them have valid SKILL.md files.

Diagnosis

probeCursorSkills scans eight roots: .cursor/skills, .agents/skills, .codex/skills and .claude/skills, first under the workspace and then under the user's home. All eight share one CursorSkillScanBudget (10,000 entries, MAX_SKILL_DEPTH = 10). When any root contains a directory that goes past the depth limit, budget.exhausted is set (apps/server/src/provider/Drivers/CursorSkills.ts:207-210). probeCursorSkills then fails the whole probe with CursorSkillsProbeError(scan-budget-exhausted) (CursorSkills.ts:268-273), even though the project roots were scanned completely first. CursorDriver.snapshotForCwd (CursorDriver.ts:235-247) turns this into a ProviderDriverError, and the registry logs "provider registry refresh failed; preserving cached providers". As a result, the composer never gets the workspace skills.

On this machine the deep tree is not a skill. It is leftover staging data from Claude's skill sync at ~/.claude/skills/synced/<id>/.staging/<pid>/pptx/pptx/scripts/office/schemas/ecma/fouth-edition, 11 levels below ~/.claude/skills. The two staging folders date from 6 days earlier, and their PIDs are no longer running. Without that folder, the deepest path under ~/.claude/skills is 8 levels, and Cursor skill discovery works.

Two behaviors make this worse:

  1. All-or-nothing failure. One over-deep or oversized directory in an unrelated user-level root discards every skill, including project skills that were already found. Returning the skills found so far (or degrading per root) would be more robust.
  2. Dot-directories are walked. .staging, .trash, .git, node_modules and similar are recursed into like any other folder. Claude's skill sync writes .staging/ and .trash/ inside ~/.claude/skills, so any user who has Claude skill sync and a deep skill package (pptx, docx or xlsx ship scripts/office/schemas/...) can hit this. Skipping dot-directories, or not recursing below a directory that already contains a SKILL.md, would avoid it.

CursorSkills.ts on main is identical to v0.0.45, so this is not fixed upstream yet.

Steps to reproduce

  1. Use the Cursor provider on any workspace that has skills in <workspace>/.agents/skills/<name>/SKILL.md.
  2. Create a directory chain 11 or more levels deep under ~/.claude/skills, for example mkdir -p ~/.claude/skills/x/.staging/1/a/b/c/d/e/f/g/h/i.
  3. Open a Cursor thread in the workspace and type $ or / in the composer.
  4. No workspace skills are listed. server.trace.ndjson shows CursorSkillsProbeError ... (scan-budget-exhausted).
  5. Remove the deep directory, then restart or refresh providers. The workspace skills appear.

Version

0.0.45 (v0.0.45, 6c8fed3)

Environment

Windows 11 Pro 10.0.26200 x64, Node v26.8.2, T3 Code desktop app with a local server, cursor-agent 2026.09.23-86fc751

Evidence

{"type":"effect-span","name":"probeCursorSkills",...,"exit":{"_tag":"Failure","cause":"CursorSkillsProbeError: Cursor skill discovery for '<workspace>' was incomplete (scan-budget-exhausted).\n    at probeCursorSkills (.../server.asar/apps/server/dist/bin.mjs:157608:79)\n    at refreshWorkspaceSnapshot (.../bin.mjs:218988:40)\n    at ws.rpc.server.refreshProviders (.../bin.mjs:100908:104)"}}
{"type":"effect-span","name":"refreshWorkspaceSnapshot",...,"exit":{"_tag":"Failure","cause":"ProviderDriverError: Provider driver 'cursor' failed to create instance 'cursor': Failed to discover Cursor skills for '<workspace>' ... [cause]: CursorSkillsProbeError ..."}}
{"type":"effect-span","name":"recoverRefreshFailure",...,"events":[{"name":"[\"provider registry refresh failed; preserving cached providers\", ...]"}]}

# Per-root scan (entries / max directory depth below the root):
<workspace>/.agents/skills      137 entries, depth 2
<workspace>/.claude/skills      137 entries, depth 2
~/.cursor/skills                  2 entries, depth 1
~/.agents/skills                  2 entries, depth 1
~/.codex/skills                  71 entries, depth 3
~/.claude/skills                557 entries, depth 11   <- synced/<id>/.staging/<pid>/pptx/pptx/scripts/office/schemas/ecma/fouth-edition

Related issues

#16866: failed workspace discovery is cached and not retried. That is related (it is why the missing skills persist), but it covers retry and caching. This issue is about why the probe fails in the first place: an unrelated over-deep user-level directory aborts all Cursor skill discovery. #2736 (closed) had a similar symptom ("Cursor/OpenCode skill discovery returns zero skills") but predates the scan budget.

Fix applied or workaround

Moved the stale ~/.claude/skills/synced/<id>/.staging folder out of the skills tree, then restarted the T3 Code desktop app. The problem will come back whenever Claude's skill sync leaves another stale .staging folder.

Filed by

Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage

Activity

  1. juliusmarminge commented on Oct 8, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Thanks for the detailed write-up. Reading the code on main (12069eefd), the diagnosis looks right.

    What the code does

    • All eight roots share one CursorSkillScanBudget: project .cursor/.agents/.codex/.claude skills first, then the same four under home (CursorSkills.ts:225-246). Limits are MAX_SKILL_DEPTH = 10 and 10,000 entries (:26-28).
    • When a directory at depth 10 has a subdirectory, the walk sets budget.exhausted = true and stops (:207-210). Running out of entries does the same (:199-202). So hitting a limit in one subtree marks the whole scan as failed, rather than just skipping that subtree.
    • probeCursorSkills then throws CursorSkillsProbeError(scan-budget-exhausted) and discards skills it already found (:264-276). CursorDriver.snapshotForCwd turns that into a ProviderDriverError (CursorDriver.ts:267-285). With no workspace snapshot, the composer appears to get no skills.
    • The walk has no dot-directory or node_modules filter (:198-211). It also keeps going below a directory that already has a SKILL.md (:163-191). That would explain why a stale ~/.claude/skills/synced/<id>/.staging/... tree can reach the depth limit.

    Why it fails hard
    Failing the probe appears to be deliberate. #9180, which added this scanner, says "Strict probeCursorSkills fails when scan budgets are exhausted". The same pattern in AntigravitySkills.ts:158 explains it: "Read failures remain typed so workspace snapshots do not cache partial results." That reasoning works for a transient read error. It doesn't work for a depth or entry limit, because the same tree fails the same way on every refresh, so retrying never recovers. A filesystem error in any root (budget.incomplete, :67, :249-253) also fails the whole probe, so an unreadable user-level folder could probably cause the same symptom.

    The other providers don't walk this deep. Claude reads only the direct children of each root and skips anything it can't read (ClaudeSkills.ts:315-347). Antigravity stops at the first SKILL.md and goes at most one level down (AntigravitySkills.ts:206-229). Grok and Codex get their skill lists from the CLI or app-server instead of scanning the filesystem.

    Possible fix (not yet verified against Cursor's own loader)

    1. When a subtree hits the depth limit, skip it instead of setting exhausted. Keep the entry budget as a real hard stop.
    2. Skip dot-directories and node_modules. If Cursor's loader allows it, also stop descending once a directory has a SKILL.md.
    3. Optionally, give each root its own budget, or return partial results with a flag, so a bad user-level root can't hide project skills.

    Related

    Workaround until then: move stale .staging or .trash folders out of ~/.claude/skills, as you did, then refresh providers.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 8, 2026
  3. m4tta commented on Oct 9, 2026

    @m4tta

    Same all-or-nothing failure here, but from the 10,000 entry budget, not the depth limit. #17389 doesn't cover this. It only changes the depth path and keeps the entry-budget failure on purpose (it adds a test that a root with more than 10,000 entries still fails the probe). So this case stays broken after that PR merges.

    What happened

    In one workspace, Cursor threads only showed /model and /usage-limits in the composer. Other workspaces were fine, so it looked intermittent.

    Cause

    A project skill writes its run output into its own folder, <workspace>/.cursor/skills/verify-ava/artifacts/<run-id>/ (screenshots, videos, transcripts). After a month that folder had 301 runs and 10,651 entries. Max depth was 8, so the depth limit wasn't involved.

    Project roots are scanned first. The budget ran out inside <workspace>/.cursor/skills (remainingEntries === 0, CursorSkills.ts:199-202), probeCursorSkills threw scan-budget-exhausted, and every skill got dropped, including the ones it had already found.

    Also, ~/.agents/skills is a symlink to ~/.claude/skills on this machine. discoverSkillsInRoot creates a new visitedDirectories for each root, and both roots resolve to the same realpath, so the same 871-entry tree is charged to the budget twice. Skills get deduped by name, but the budget doesn't.

    Per-root scan before the workaround

    <workspace>/.cursor/skills   10,911 entries (10,651 in verify-ava/artifacts), depth 8
    <workspace>/.claude/skills        8 entries
    ~/.cursor/skills                  1 entry
    ~/.agents/skills                871 entries (symlink to ~/.claude/skills)
    ~/.codex/skills                  79 entries
    ~/.claude/skills                871 entries
    

    Trace

    {"name":"probeCursorSkills",...,"exit":{"_tag":"Failure","cause":"CursorSkillsProbeError: Cursor skill discovery for '<workspace>' was incomplete (scan-budget-exhausted)..."}}
    {"name":"refreshWorkspaceSnapshot",...,"cause":"ProviderDriverError: Provider driver 'cursor' failed to create instance 'cursor': Failed to discover Cursor skills for '<workspace>'..."}
    {"name":"recoverRefreshFailure",...,"provider registry refresh failed; preserving cached providers"}
    

    Repro

    1. Workspace with any skill at .cursor/skills/<name>/SKILL.md.
    2. mkdir -p .cursor/skills/<name>/artifacts && for i in $(seq 1 11000); do touch .cursor/skills/<name>/artifacts/f$i; done
    3. Open a Cursor thread in that workspace and type /. No skills, and the trace shows scan-budget-exhausted.

    Suggestions beyond the triage comment

    • Don't fail the probe on entry exhaustion either. Return what was found, or give each root its own budget, so one big project skill can't hide everything else.
    • Stop recursing once a directory has a SKILL.md, if Cursor's loader allows it. Then node_modules, build output or run artifacts next to a SKILL.md wouldn't count.
    • Share visitedDirectories (or a set of root realpaths) across roots so a symlinked root is only charged once.

    Workaround

    Deleted the old run folders under artifacts/ and refreshed providers.

    Environment

    T3 Code 0.0.45 (v0.0.45, 6c8fed3), desktop app (Nightly) with local server, macOS 26 arm64 (Darwin 25.5.0), Node v26.8.2.


    Posted via t3 triage with Claude Code (Claude Opus 5.5, claude-opus-5-5).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions