SQLite Mutex Contention Causes Main Thread Blocking and UI Freezes

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Problem Description

Cursor IDE becomes completely unresponsive during file operations, with the main thread blocked in SQLite database operations. Analysis using macOS sample command reveals severe mutex contention where 1,518 samples (68% of main thread activity) are waiting on SQLite mutex locks.

Symptoms

  • UI Freezes: Cursor becomes unresponsive for several seconds to minutes
  • High Memory Usage: 1.1-1.4GB during blocking (vs. normal 167MB when idle)
  • Main Thread Blocked: Entire application freezes during database operations
  • Occurs During:
    • File system operations (creating/editing many files)
    • Bulk operations (search/replace across multiple files)
    • Projects with many files (100+ YAML files observed)
    • Extension operations that access the file system

Technical Analysis

Sample Report Details:

  • Process: Cursor (pid 51820)
  • Date/Time: 2025-11-30 20:17:50
  • Memory: 1.1GB (peak: 1.1GB)
  • Sampling: 1ms intervals, 2,230 total samples

Blocking Call Stack:

Main Thread (2,230 samples):
  uv_run (Electron Framework)
    → uv__io_poll
      → uv__work_done
        → napi_async_cleanup_hook_handle__::Hook
          → node_sqlite3::Statement::Work_AfterGet
            → node_sqlite3::Statement::Process
              → node_sqlite3::Statement::Finalize_
                → sqlite3_finalize (vscode-sqlite3.node)
                  → _pthread_mutex_firstfit_lock_slow
                    → _pthread_mutex_firstfit_lock_wait
                      → __psynch_mutexwait [BLOCKED - 1,518 samples]

Key Findings:

  • 1,518 samples (68%) waiting on __psynch_mutexwait in SQLite finalize operations
  • 692 samples in active SQLite operations (sqlite3VdbeReset, sqlite3VdbeHalt)
  • Multiple threads competing for the same SQLite database lock
  • No connection pooling or statement caching observed
  • Synchronous database operations on main thread

Root Cause

The issue appears to be caused by:

  1. Lack of Connection Pooling: Multiple threads accessing SQLite without proper connection management
  2. No Statement Caching: Statements are finalized and recreated instead of being reused
  3. Synchronous Operations: Database operations block the main thread instead of using async/worker threads
  4. Mutex Contention: Multiple threads compete for the same database lock, causing cascading blocks

System Information

  • OS: macOS 15.6.1 (24G90)

  • Architecture: ARM64 (Apple Silicon)

  • Cursor Version: 2.1.42

  • Parent Process: launchd [1]

  • Analysis Tool: macOS sample command

  • Main thread completely blocked waiting for SQLite mutex locks

  • UI freezes for extended periods (seconds to minutes)

  • High memory usage during blocking

  • Multiple threads waiting on the same database lock

Impact

Severity: High

  • Makes Cursor unusable during file operations
  • Significantly impacts developer productivity
  • Affects any workflow involving bulk file operations
  • Worse on projects with many files

Workarounds Attempted

  1. :white_check_mark: Reduced file system activity (using filtered commands)
  2. :white_check_mark: Batched operations with delays
  3. :white_check_mark: Used CLI commands instead of direct file edits
  4. :warning: Limited effectiveness - issue persists during normal usage

Additional Context

This issue is distinct from general performance problems because:

  • It’s specifically related to SQLite mutex contention
  • The blocking is measurable and reproducible
  • It occurs during specific operations (file system access)
  • The call stack clearly shows SQLite finalize operations blocking

Comparison with Normal Operation:

  • Idle State: 167MB memory, no blocking observed
  • Blocking State: 1.1GB memory, 1,518 samples blocked on mutex

Related Components:

  • vscode-sqlite3.node (SQLite native module)
  • Electron Framework (Node.js integration)
  • VS Code file indexing system

Suggested Solutions

  1. Implement Connection Pooling: Limit concurrent SQLite connections
  2. Statement Caching: Reuse prepared statements instead of finalizing
  3. Async Operations: Move SQLite operations to worker threads
  4. Lock Timeouts: Add timeouts to prevent indefinite blocking
  5. WAL Mode: Consider Write-Ahead Logging for better concurrency
  6. Rate Limiting: Throttle file indexing operations

Attachments

  • Sample report: cursor-blocking.txt (available upon request)
  • Performance analysis: Detailed analysis of 8 sample reports showing the pattern

Steps to Reproduce

  1. Open a project with many files (100+ YAML files, or similar)
  2. Perform bulk file operations:
    • Create/edit multiple files in quick succession
    • Use search/replace across many files
    • Trigger file system indexing operations
  3. Observe UI freezing during operations
  4. (Optional) Capture blocking state:
    # Find Cursor process ID
    ps aux | grep -i cursor
    
    # Sample the process (replace PID)
    sample <PID> -mayDie -f cursor-blocking.txt
    

Expected Behavior

  • SQLite operations should not block the main thread
  • Database operations should use connection pooling
  • Statement caching should prevent frequent finalize/recreate cycles
  • Mutex contention should be minimized through proper locking strategies
  • UI should remain responsive during file operations

Operating System

MacOS

Current Cursor Version (Menu → About Cursor → Copy)

Version: 2.1.42 (Universal)
VSCode Version: 1.105.1
Commit: 2e353c5f5b30150ff7b874dee5a87660693d9de0
Date: 2025-12-01T02:18:26.377Z (2 hrs ago)
Electron: 37.7.0
Chromium: 138.0.7204.251
Node.js: 22.20.0
V8: 13.8.258.32-electron.0
OS: Darwin arm64 24.6.0

Does this stop you from using Cursor

Sometimes - I can sometimes use Cursor

hi @lanceman please clarify following as it is not clear from your long descrption

  • Is this an SQLite DB that you use in your project or is it about Cursors access to its own DB.
  • How many Cursor windows do you have open in total and how many per project max?
  • Was this during an update? (restarting helps free up locks)
  • Does the issue go away after restarting MacOS? (in some cases this is the solution)
  • Is anything else at same time accessing the same database? If you have it in another app open or is terminal accessing it).
  • Are there any other heavy filesystem operations occurring at same time?
1 Like

Thanks Condor.

I’m assuming this is Cursor’s own DB. When I encounter the issue, I am not working with any DB resources.

Cursor windows - usually only one, sometimes a couple of chats, but they are historical not active

I don’t believe it was during an update, but understand why you’d inquire if it was. definitely noticed some slowness during updates in the past.

Rebooting helps, but doesn’t eliminate the issue. I’m assuming it is likely due to indexing files for speed.

The operations I’m performing involve a lot of I/O on hundreds of files at once. it seems like the indexer may be getting overwhelmed but never times out so is subject to hangs.

I filed the issue after sampling processes and seeing indefinite waits. you can download the thread sample from github if you see the linked issue

Indexing does require time and performance. Are you able to exclude files that do not need to be indexed with .cursorindexingignore to relieve some of the update pressure?

I did discover the .cursorignore configuration for the PolicyWatcher which has allowed me to be more selective about the files that Cursor concerns itself with. I wasn’t aware of the .curosrindexignore which may be a more fine-grained option. I appreciate the suggestion and will check it out.

In general, you can consider this issue resolved from my perspective. I’m able to work around the issue sufficiently.

I would still recommend wrapping the indexing processes with a timeout or some other ‘fail faster’ mechanism to minimize the chances for things to stack up significantly in blocking I/O situations which seems to cause seizure of the UI. Perhaps the issue is in a library that the Cursor team doesn’t own, in which case, I would just make a note and evaluate options for swapping it out or re-writing to provide a more reliable mechanism.

Thanks again!