fix(mcp): handle Neo4j 5.x index creation race condition
Summary
Fixes the MCP server crash on startup when using Neo4j 5.x due to EquivalentSchemaRuleAlreadyExists error during index creation.
Problem
When build_indices_and_constraints() runs, it executes ~19 index creation queries in parallel via semaphore_gather(). Neo4j 5.x has a race condition where concurrent CREATE INDEX ... IF NOT EXISTS statements can fail even though the guard clause should prevent errors.
neo4j.exceptions.ClientError: {neo4j_code: Neo.ClientError.Schema.EquivalentSchemaRuleAlreadyExists}
{message: An equivalent index already exists, 'Index( id=10, name='relation_uuid', type='RANGE', ...)'}
Solution
Wrap the build_indices_and_constraints() call in a try/except that catches this specific error and continues, since the indexes are successfully created by whichever parallel query completes first.
try:
await self.client.build_indices_and_constraints()
except Exception as idx_error:
if 'EquivalentSchemaRuleAlreadyExists' in str(idx_error):
logger.warning(
'Index creation race condition detected (Neo4j 5.x issue). '
'Indexes likely already exist. Continuing...'
)
else:
raise
Testing
- Tested with Neo4j 5.26.0 and graphiti-core 0.24.1
- Server initializes successfully with warning logged
- All Graphiti operations (add_memory, search_nodes, etc.) work correctly
- Indexes are properly created in Neo4j
Related Issues
Fixes #353
Checklist
- [x] Fix handles only the specific
EquivalentSchemaRuleAlreadyExistserror - [x] Other exceptions are re-raised
- [x] Warning message logged for visibility
- [x] No impact on FalkorDB backend
All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.
I have read the CLA Document and I hereby sign the CLA
TLDR Version: Neo4j 5.x Index Creation Race Condition
- here's additional context in case different approaches want to be examined longer term. I went with what was simplest and least risky in the near term that allowed me to leverage the MCP functionality.
(thanks to the Graphiti team for all their hard work)
Summary
This PR fixes a race condition that causes the Graphiti MCP server to crash on startup when using Neo4j 5.x as the database backend. The issue occurs during index creation in build_indices_and_constraints().
Problem Description
Error Message
neo4j.exceptions.ClientError: {neo4j_code: Neo.ClientError.Schema.EquivalentSchemaRuleAlreadyExists}
{message: An equivalent index already exists, 'Index( id=10, name='relation_uuid', type='RANGE',
schema=()-[:RELATES_TO {uuid}]-(), indexProvider='range-1.0' )'.}
Root Cause
- Parallel Index Creation:
graphiti_core/driver/neo4j_driver.pyruns all index creation queries in parallel viasemaphore_gather()for performance - Neo4j 5.x Race Condition: Neo4j 5.x has a known issue where concurrent
CREATE INDEX ... IF NOT EXISTSstatements can fail even though theIF NOT EXISTSclause should prevent errors - MCP Server Crash: The MCP server wrapper doesn't handle this error, causing the entire server to fail initialization
Technical Details
The index creation code in graphiti_core/driver/neo4j_driver.py (lines 91-108):
async def build_indices_and_constraints(self, delete_existing: bool = False):
# ... deletion logic ...
await semaphore_gather(
*[self.execute_query(query) for query in NEO4J_INDEX_QUERIES],
*[self.execute_query(query) for query in NEO4J_CONSTRAINT_QUERIES],
)
This executes ~19 index creation queries in parallel. When Neo4j 5.x processes these concurrently:
- Multiple queries may try to create the same index simultaneously
- Even with
IF NOT EXISTS, Neo4j throwsEquivalentSchemaRuleAlreadyExists - The first query that completes "wins", but others fail despite the guard clause
Affected Versions
- Neo4j: 5.x (tested with 5.26.0)
- graphiti-core: All versions up to 0.24.1
- Not affected: FalkorDB backend (handles "already indexed" errors separately)
Solution
Wrap the build_indices_and_constraints() call in the MCP server with a try/except that catches this specific error and continues operation, since the indexes will have been created successfully by whichever parallel query completed first.
Code Change
File: mcp_server/src/graphiti_mcp_server.py (lines 281-292)
# Before:
await self.client.build_indices_and_constraints()
# After:
# Build indices - wrap in try/except to handle Neo4j 5.x race condition
# with parallel IF NOT EXISTS index creation
try:
await self.client.build_indices_and_constraints()
except Exception as idx_error:
if 'EquivalentSchemaRuleAlreadyExists' in str(idx_error):
logger.warning(
'Index creation race condition detected (Neo4j 5.x issue). '
'Indexes likely already exist. Continuing...'
)
else:
raise
Why This Fix Works
- Indexes are created: The parallel execution still creates all indexes - the error only occurs because some queries "see" indexes created by sibling queries
- Idempotent operation: Index creation is idempotent - if the index exists, the operation is a no-op
- Non-destructive: We only catch this specific error; other errors are re-raised
- Informative logging: Users see a warning explaining what happened
Testing
Before Fix
$ docker compose -f docker-compose-neo4j.yml up -d
$ docker logs docker-graphiti-mcp-1
# Output:
neo4j.exceptions.ClientError: {neo4j_code: Neo.ClientError.Schema.EquivalentSchemaRuleAlreadyExists}
# Container exits with code 1
After Fix
$ docker compose -f docker-compose-neo4j.yml up -d
$ docker logs docker-graphiti-mcp-1
# Output:
2025-11-24 - graphiti_mcp_server - WARNING - Index creation race condition detected (Neo4j 5.x issue). Indexes likely already exist. Continuing...
2025-11-24 - graphiti_mcp_server - INFO - Successfully initialized Graphiti client
$ curl http://localhost:8000/health
{"status":"healthy","service":"graphiti-mcp"}
Verification
# Confirm indexes were created
$ docker exec docker-neo4j-1 cypher-shell -u neo4j -p demodemo "SHOW INDEXES" | wc -l
27 # All indexes present
# Confirm data operations work
$ curl -X POST http://localhost:8000/mcp -d '...' # add_memory, search_nodes, etc.
# All operations successful
Related Issues
-
#353 - The graphiti is not working properly with initial setup
- Reports
IndexOrConstraintAlreadyExistsnotifications during startup - No resolution provided
- Reports
-
#289 - Received notification from DBMS server
- Property key warnings during initialization
- Closed but related to schema initialization issues
Alternative Solutions Considered
1. Sequential Index Creation
Approach: Modify neo4j_driver.py to run index queries sequentially instead of in parallel.
Pros: Eliminates race condition at source Cons:
- Slower startup (~19 queries × network latency)
- Requires changes to graphiti-core
- May impact other use cases that benefit from parallel execution
2. Add Retry Logic
Approach: Retry failed index creation queries individually.
Pros: More granular error handling Cons:
- Complex implementation
- Still requires error handling in MCP server
- Unnecessary since indexes are created by parallel queries
3. Pre-create Indexes via Script
Approach: Run index creation before starting the MCP server.
Pros: Separates concerns Cons:
- Requires additional setup step
- Poor developer experience
- Doesn't address the fundamental issue
Chosen Solution: Exception Handling in MCP Server
Rationale:
- Minimal code change
- Doesn't affect graphiti-core
- Handles the specific failure mode gracefully
- Maintains performance benefits of parallel execution
- Clear logging for debugging
Checklist
- [x] Fix handles the specific
EquivalentSchemaRuleAlreadyExistserror - [x] Other exceptions are re-raised (not silently swallowed)
- [x] Warning message is logged for visibility
- [x] Server initializes successfully after fix
- [x] All Graphiti operations work correctly (add_memory, search_nodes, etc.)
- [x] Indexes are properly created in Neo4j
- [x] No impact on FalkorDB backend