Skip to content

Fix Neo4j delete: IN TRANSACTIONS is illegal via execute_query - #9

Merged
ShreeBohara merged 1 commit into
mainfrom
fix/neo4j-delete-transaction
Aug 11, 2026
Merged

Fix Neo4j delete: IN TRANSACTIONS is illegal via execute_query#9
ShreeBohara merged 1 commit into
mainfrom
fix/neo4j-delete-transaction

Conversation

@ShreeBohara

Copy link
Copy Markdown
Owner

Docker is available now, so I ran the Cypher merged in #6 against a real Neo4j. It does not work.

Neo.DatabaseError.Transaction.TransactionStartFailed
A query with 'CALL { ... } IN TRANSACTIONS' can only be executed in an implicit
transaction, but tried to execute in an explicit transaction.

driver.execute_query() runs inside an explicit transaction, and IN TRANSACTIONS is only legal in an implicit one. So delete_repository always raised — and sync_repository calls it first, which means the entire Neo4j ingest path failed 100% of the time.

It looked healthy only because the caller swallows sync failures by design and falls back to SQL. That fallback is what kept this invisible.

The fix

delete_repository loops bounded LIMIT batches. That keeps each transaction small without needing implicit-transaction semantics, and it drops the CALL (n) { ... } scoped-variable form, which only exists from Neo4j 5.23. It now returns the number of nodes deleted, so a caller can distinguish "nothing there" from "did not run".

Why the unit tests didn't catch it

They assert "DETACH DELETE" in query against a fake driver. That still passes. A fake driver doesn't enforce transaction semantics, so no amount of mocking would have found this — the bug was invisible to mocks by construction.

Added tests/integration/test_neo4j_live.py: 9 tests against a real server, skipped unless NEO4J_TEST_URI/NEO4J_TEST_PASSWORD are set, so CI and a machine without Docker stay green. Beyond the regression they cover degree via COUNT {}, transitive traversal in both directions, shortest-path directionality, cycle detection, re-sync replacing rather than unioning, cross-repository isolation, and a delete larger than one batch.

Verification

Against neo4j:5-community, kernel 5.26.29:

[PASS] verify_connectivity          [PASS] shortest_path dist=2
[PASS] ensure_schema x2 idempotent  [PASS] shortest_path None when unreachable
[PASS] sync_repository              [PASS] import_cycles
[PASS] degree via COUNT{}           [PASS] re-sync REPLACES not unions
[PASS] edges()                      [PASS] delete_repository returns count
[PASS] transitive reach             [PASS] subgraph gone
[PASS] blast radius (reverse)       [PASS] delete is repo-scoped
=== 14/14 passed ===
Gate Result
Live Neo4j tests 9/9 pass
Full suite, no server 142 passed, 9 skipped, 0 failed
ruff check src tests clean

Everything #6 claimed is now actually true, rather than asserted. The remaining honest caveat: this validates the store in isolation, not a full index-then-graph run through the API.

🤖 Generated with Claude Code

The Cypher merged in the graph-store PR had never run against a server. It does not work.

    Neo.DatabaseError.Transaction.TransactionStartFailed
    A query with 'CALL { ... } IN TRANSACTIONS' can only be executed in an implicit
    transaction, but tried to execute in an explicit transaction.

driver.execute_query() runs inside an explicit transaction and IN TRANSACTIONS is only
legal in an implicit one, so delete_repository always raised. sync_repository calls it
first, so the entire ingest path failed 100% of the time -- it only looked healthy because
the caller swallows sync failures by design and falls back to SQL.

delete_repository now loops bounded LIMIT batches instead. That keeps each transaction
small without needing implicit-transaction semantics, and it also drops the
CALL (n) { ... } scoped-variable form, which only exists from Neo4j 5.23. It returns the
number of nodes deleted so a caller can tell the difference between "nothing there" and
"did not run".

WHY THE UNIT TESTS DID NOT CATCH THIS
They assert `"DETACH DELETE" in query` against a fake driver. That still passes. A fake
driver does not enforce transaction semantics, so no amount of mocking could have found
it. Added tests/integration/test_neo4j_live.py: 9 tests against a real server, skipped
unless NEO4J_TEST_URI and NEO4J_TEST_PASSWORD are set, so CI and a laptop without Docker
stay green. They cover the regression directly plus degree, transitive traversal in both
directions, shortest-path direction, cycle detection, re-sync replacing rather than
unioning, cross-repository isolation, and a delete larger than one batch.

Verified against neo4j:5-community (kernel 5.26.29): 9/9 live tests pass, and the full
suite is 142 passed with the 9 live tests skipping cleanly when no server is configured.
ruff clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
codebaseqa-web Ready Ready Preview Aug 11, 2026 7:21pm

@ShreeBohara
ShreeBohara merged commit 5340eca into main Aug 11, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant