Cursor Indexing Never Completes

Index spinner runs for hours with no progress — usually a huge repo, wrong ignore config, symlink loops, or permission failures.

Settings → Features → Codebase Indexing reads “Indexing… 3,432 of 1,200,000 files.” Half an hour later the count hasn’t moved; sometimes the spinner just turns at 0%. The most common cause isn’t a Cursor bug — it’s .cursorignore missing node_modules / build outputs, so a million generated files have been thrown into the scanner. After that: symlink loops, network mounts, permissions.

Fix by figuring out which class of files is stuck, then exclude precisely.

Common causes

1. node_modules / dist / .next / build not ignored

Cursor honors .gitignore by default, but lots of teams’ .gitignore lists node_modules and misses dist/, coverage/, .next/, .turbo/, __pycache__/, venv/. .next/cache/webpack alone can be 500k files in a Next.js monorepo.

How to judge:

git status --ignored | head -20
du -sh node_modules .next dist build coverage 2>/dev/null | sort -h

The biggest directory not in gitignore is the suspect.

packages/web/node_modules/internal-pkg -> ../../shared, and shared symlinks back. Scanner recurses forever.

How to judge:

find . -type l 2>/dev/null | head -20

Any symlink pointing to a parent directory.

3. Repo exceeds Cursor’s default cap

Single-repo indexing roughly maxes out at 10k-500k files depending on plan. Beyond that, Cursor either gives up silently or retries forever. Real-LOC monorepos hit this fast.

How to judge:

git ls-files | wc -l       # tracked
find . -type f | wc -l     # all, including ignored

If the second number is >100k, you’re past scale.

4. Permission denied

A directory owned by root (system path, Docker volume) the Cursor process can’t read. The indexer hangs waiting.

How to judge:

find . -not -readable 2>/dev/null | head

5. Network drive / WSL boundary

Project on SMB / NFS / Dropbox / iCloud sync — file IO latency jumps to 100ms+, indexer throughput drops two orders of magnitude.

How to judge: df -h . for mount type; path like /mnt/c/... or ~/Library/Mobile Documents/....

6. Cursor’s indexer service hung

Extension host crashed or the index worker died, but the UI doesn’t update.

How to judge: Cmd+Shift+P → “Developer: Show Running Extensions” — Cursor’s index worker unresponsive.

Before you start

  • Confirm whether it’s this one repo or every repo; the latter is usually a Cursor install or account problem.
  • Commit .cursorignore before editing so you can roll back.
  • Note Cursor version, repo file count, OS, subscription tier.

Info to collect

  • Cursor version, plan (Hobby / Pro / Business).
  • Output of git ls-files | wc -l and find . -type f | wc -l.
  • Full .cursorignore / .gitignore.
  • Screenshot of Settings → Features → Codebase Indexing.
  • Help → Toggle Developer Tools → Console indexing-related logs.

Shortest fix path

Ordered by impact.

Step 1: Write a proper .cursorignore

At repo root, create / edit .cursorignore:

# Dependencies
node_modules
.pnpm
.yarn
vendor
__pycache__
venv
.venv

# Build output
dist
build
out
.next
.nuxt
.turbo
.svelte-kit
coverage
target
.gradle

# Caches
.cache
.parcel-cache
.eslintcache
*.tsbuildinfo

# Data files
*.parquet
*.bin
*.gz
*.zip
*.sqlite

# Logs
*.log
logs

# Lockfiles (optional — large but sometimes wanted)
# package-lock.json
# pnpm-lock.yaml

Step 2: Trigger a reindex

Cmd+Shift+P → “Cursor: Resync Index” (or “Rebuild Codebase Index”). Then Cmd+Shift+P → “Developer: Reload Window.” Watch progress for 5-30 minutes.

find . -type l -exec ls -l {} \; 2>/dev/null | grep -E "\.\./.*\.\./"

For each parent-pointing symlink, either add it to .cursorignore or convert to a native npm / yarn workspace link.

Step 4: Narrow the working directory

Don’t open Cursor at a 50k-file monorepo root. File → Open Folder → pick the package you’re actually in this week (apps/web). Indexing surface shrinks by an order of magnitude.

Step 5: Move off network drive / WSL boundary

git clone to ~/repo (APFS, WSL ext4, native Linux). Don’t keep active projects in Dropbox / iCloud sync directories.

Step 6: Nuclear option — clear cache and rebuild

# macOS / Linux
rm -rf ~/Library/Application\ Support/Cursor/User/workspaceStorage/*
# Windows
# rmdir /s %APPDATA%\Cursor\User\workspaceStorage

This wipes every workspace’s index cache; every repo will reindex on next open. Use only as a last resort.

How to verify the fix

  • Restart Cursor and reproduce — confirms it isn’t transient session state.
  • Indexing progress reaches 100% in a reasonable time (≤ 30 minutes).
  • Composer can answer “List every file under src/api/” with the real list, proving index coverage.

If it still fails

  • Reduce repro: open a fresh empty repo, see if that also stalls.
  • Roll back the most recent Cursor upgrade or .cursorignore change.
  • Search forum.cursor.com for “indexing never completes”; include file count + OS + ignore content.
  • Grab Developer Tools → Console indexing logs and post to Bug Reports.

Prevention

  • First thing on a new repo: drop in a .cursorignore. Don’t wait to be bitten.
  • Keep .cursorignore and .gitignore differentiated: generated types / schemas the model needs stay in; node_modules always out.
  • Don’t commit datasets (parquet / sqlite / huge JSON) — use git-lfs or S3.
  • Always run on native filesystems; never iCloud / Dropbox / network mounts.
  • Quarterly git ls-files | wc -l. Past 50k files, consider splitting the monorepo.

Tags: #Troubleshooting #Cursor #Debug #Indexing stuck