Cursor Stuck Indexing: Make It Skip node_modules and Build Output

Cursor stuck on Indexing? 90% of the time it's trying to index node_modules, dist, .next, or cache directories. Here's the .cursorignore template and a clean rebuild flow.

Open a moderately large project and Cursor’s status bar reads Indexing 5% for half an hour, or finishes indexing and immediately re-triggers, with your fans at max. 90% of the time Cursor isn’t crashed — it’s trying to index node_modules/, dist/, .git/, and other directories that should never be indexed. On a mid-size frontend project those alone easily exceed a million files, which no consumer SSD will index quickly. This article gives the .cursorignore template that fixes it, the rebuild command, and how to confirm the index is healthy.

Common causes

Ordered by hit rate, highest first.

1. node_modules/ isn’t excluded

Most common. A mid-size frontend’s node_modules/ is 300k–800k files, two orders of magnitude more than your source. Cursor should skip it by default, but odd workspace configs or symlinks occasionally pull it back in.

How to spot it: find node_modules -type f | wc -l. If > 100,000 and Cursor stays on Indexing forever, this is almost certainly it.

2. Build output directories (dist/ / build/ / .next/ / .astro/)

On projects with hot reload, build output is rewritten constantly, so Cursor sees file changes and re-indexes, creating an “index finished → detected changes → re-index” loop.

How to spot it: Indexing re-triggers repeatedly while your dev server is running, and stops the moment you kill the dev server.

3. .git/ object directory

Large repos can have hundreds of thousands of pack files in .git/objects/. Cursor usually skips it, but cloned mirrors or LFS-heavy repos sometimes leak through.

How to spot it: du -sh .git/. If > 500MB and not explicitly ignored, suspect it.

4. Cache and log directories

.cache/, .turbo/, .vercel/, logs/, coverage/ are written to constantly and trigger the same re-index loop.

How to spot it: When Cursor re-indexes, sort Activity Monitor by disk I/O for the Cursor process and see which directory is being scanned.

5. Monorepo subpackages all contribute artifacts

apps/web/.next, apps/api/dist, packages/ui/storybook-static all indexed together stack to millions of files. Each subpackage needs its own .cursorignore, or the root needs wildcard rules.

How to spot it: find . -name dist -o -name .next -o -name build -type d. If > 3 hits with no ignore coverage, that’s the problem.

Shortest path to fix

Step 1: Write a thorough .cursorignore

At project root, create or edit .cursorignore:

# Dependencies
node_modules/

# Version control
.git/

# Build output
dist/
build/
out/
.next/
.astro/
.svelte-kit/
.nuxt/
.output/

# Caches
.cache/
.turbo/
.vercel/
.parcel-cache/
.pytest_cache/
__pycache__/

# Test coverage
coverage/
.nyc_output/

# Logs and lockfiles
*.log
*.lock

For monorepos, drop a local .cursorignore into each subpackage:

# packages/web/.cursorignore
.next/
.turbo/

Step 2: Force a full index rebuild

.cursorignore changes don’t auto-rebuild — you must trigger it.

Cmd/Ctrl + Shift + P → "Cursor: Reset Index"

Or more thoroughly (recommended for the re-index loop case):

# Quit Cursor, then wipe the workspace index cache
rm -rf ~/Library/Application\ Support/Cursor/User/workspaceStorage/*/cursorIndex
# Linux:
rm -rf ~/.config/Cursor/User/workspaceStorage/*/cursorIndex
# Relaunch Cursor

Step 3: Wait and confirm

Restart Cursor and reopen the project:

  1. Status bar briefly shows Indexing 0%
  2. Within 1–3 minutes it switches to Indexed (mid-size projects); 5–10 minutes for large monorepos
  3. If still incomplete after 10 minutes, go back to Step 1 and check for missed artifact directories

Open Settings → Features → Codebase Indexing and check “Files Indexed.” Healthy mid-size projects are typically 5k–30k. If you’re still at 100k+, your ignore list isn’t taking effect.

Step 4: Isolate dev server output too

If indexing finishes but the dev server keeps triggering re-runs, add dev output paths to .cursorignore. Vite / Webpack let you direct build output into a single ignored directory:

// vite.config.ts
export default {
  build: {
    outDir: 'dist',  // already in .cursorignore
  },
  cacheDir: '.cache/vite',  // already in .cursorignore
}

Prevention

  • Commit .cursorignore the moment you create a new repo, and maintain it alongside .gitignore
  • Don’t scatter cache / build output dirs in odd locations; keep them at the root
  • For monorepos, drop a .cursorignore in each subpackage — don’t rely on root wildcards to cover all variants
  • After installing a new dependency, changing build tools, or adding a generator, manually Reset Index — don’t wait for a loop
  • Always ignore directories your dev server or hot reload writes to frequently (.cache/, .turbo/)

Tags: #Cursor #AI coding #Debug