Migrates Neo4j driver code and Cypher queries from older versions (4.x, 5.x) to current (2025.x/2026.x, Cypher 25). Covers Python, JavaScript/Node.js, Java, .NET, and Go drivers — package renames, removed APIs, version requirements, diff-ready fixes. Also handles Cypher syntax migration — QPE paths, CALL subqueries, id() → elementId(), PERIODIC COMMIT → CALL IN TRANSACTIONS, and all Cypher 25 removals. Does NOT write new Cypher queries — use neo4j-cypher-skill. Does NOT cover DB administration or server ops — use neo4j-cli-tools-skill. Does NOT provision new Neo4j instances — use neo4j-getting-started-skill.
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
neo4j-cypher-skillneo4j-cli-tools-skillneo4j-getting-started-skillBefore starting, ask:
package.json, requirements.txt, pom.xml, *.csproj, go.modIf target is >= 2025.06: ask whether Cypher 5 or Cypher 25 dialect will be used (both supported; Cypher 25 is the new default).
# Find Cypher strings (queries embedded in source)
grep -rn "MATCH\|MERGE\|CREATE\|CALL\|RETURN" --include="*.py" --include="*.js" \
--include="*.ts" --include="*.java" --include="*.cs" --include="*.go" \
--include="*.cypher" --include="*.cql" . | grep -v ".git"
# Find dependency files
find . -name "requirements*.txt" -o -name "package.json" -o -name "pom.xml" \
-o -name "*.csproj" -o -name "go.mod" | grep -v ".git" | grep -v node_modulesCollect findings before modifying anything. List all deprecated patterns found.
Apply to every Cypher string, .cypher file, OGM/SDN @Query annotation, and template literal.
| Old syntax | Cypher 25 replacement | Notes |
|---|---|---|
[:REL*1..5] | (()-[:REL]->()){1,5} | QPE quantifier group |
[:REL*] | (()-[:REL]->()){1,} | Unbounded QPE |
[:REL*0..5] | (()-[:REL]->()){0,5} | Zero-hop allowed |
shortestPath((a)-[*]->(b)) | SHORTEST 1 (a)(()-[]->()){1,}(b) | QPE shortest path |
allShortestPaths((a)-[*]->(b)) | ALL SHORTEST (a)(()-[]->()){1,}(b) | QPE all-shortest |
id(n) | elementId(n) | Returns string, not int |
CALL { WITH x ... } | CALL (x) { ... } | Explicit import syntax |
PERIODIC COMMIT | CALL (...) { ... } IN TRANSACTIONS OF 1000 ROWS | Batched writes |
-- comment | // comment | SQL comment invalid |
SET n = r (structural val) | SET n = properties(r) | Extract map first |
MERGE (a {x:1})-[:T]->(b {x:a.x}) | Rewrite — cross-entity MERGE refs disallowed | Split into MATCH+MERGE |
CREATE INDEX ... OPTIONS { indexProvider: ... } | Remove indexProvider from OPTIONS | Provider selection removed |
db.create.setVectorProperty(...) | Use native VECTOR property type | Procedure removed |
dbms.upgrade() / dbms.upgradeStatus() | Use cypher-shell :sysinfo | Procedures removed |
USE composite.'1' | USE `composite.1` | Backtick entire qualified name |
RETURN 1 as my$Identifier | RETURN 1 as `my$Identifier` | $ removed from unescaped ids |
| Version | Syntax |
|---|---|
| Neo4j < 2026.01 | CALL db.index.vector.queryNodes(idx, k, $emb) YIELD node, score |
| Neo4j >= 2026.01 | SEARCH n IN (VECTOR INDEX idx FOR $emb LIMIT k) SCORE AS score |
CYPHER 25 to every top-level queryCYPHER 5 (keeps deprecated forms working)id(n) → elementId(n) CaveatselementId() returns a string (e.g., "4:abc123:0"), not an integer. Fix downstream code that stores or compares it as int. Do NOT use elementId() values for numeric operations.
- WHERE id(n) = $nodeId # $nodeId was integer
+ WHERE elementId(n) = $nodeId # $nodeId must now be stringPick the section(s) matching languages found in Step 1.
Min requirements: Python >= 3.10, neo4j package (6.0+, released Jan 12 2026)
# requirements.txt
- neo4j-driver>=5.0.0
+ neo4j>=6.0.0neo4j-driver package is deprecated since 6.0 and receives no further updates.
| Old | New | Notes |
|---|---|---|
session.read_transaction(fn) | session.execute_read(fn) | |
session.write_transaction(fn) | session.execute_write(fn) | |
session.last_bookmark() | session.last_bookmarks() | Returns Bookmarks (plural) |
Bookmark class | Bookmarks class | |
TRUST_ALL_CERTIFICATES | trusted_certificates=TrustAll() | |
TRUST_SYSTEM_CA_SIGNED_CERTIFICATES | trusted_certificates=TrustSystemCAs() | |
neo4j.conf, neo4j.data modules | Removed — use top-level neo4j.* |
Drivers and sessions no longer auto-close on GC. Add explicit cleanup:
- driver = GraphDatabase.driver(URI, auth=(USER, PASS))
- # used without explicit close
+ with GraphDatabase.driver(URI, auth=(USER, PASS)) as driver:
+ with driver.session(database="neo4j") as session:
+ ...Or call .close() explicitly. Repeated .close() is a no-op.
| Old exception | New exception | Trigger |
|---|---|---|
ClientError (for timeout) | ConnectionAcquisitionTimeoutError | Acquisition timeout |
AuthError (invalid config) | ConfigurationError | Invalid auth params |
ValueError raised for Query obj | TypeError | Passing Query to Transaction.run |
execute_query(), GraphDatabase.driver(), neo4j+s:// URI, auth=(user, pw), session run().
Full changelog: references/python-driver.md
Min requirements: Node.js >= 18 (check engines in driver's package.json); package neo4j-driver
| Old | New | Notes |
|---|---|---|
session.readTransaction(fn) | session.executeRead(fn) | |
session.writeTransaction(fn) | session.executeWrite(fn) | |
session.lastBookmark() | session.lastBookmarks() | Returns array |
resultSummary.updateStatistics | resultSummary.counters | |
driver.verifyConnectivity() → ServerInfo | driver.getServerInfo() → ServerInfo | verifyConnectivity() now returns void |
| Old | New |
|---|---|
isRetriableError(err) | isRetryable(err) |
neo4jError.retriable | neo4jError.retryable |
notificationCategory | notificationClassification |
Neo4j integers map to neo4j.Integer (64-bit). Use .toNumber() or .toInt() for arithmetic. Large values (> 2^53) lose precision — store as string with .toString().
// Safe pattern
const count = record.get('count').toNumber()Full changelog: references/javascript-driver.md
Min requirements: Java 21; Maven org.neo4j.driver:neo4j-java-driver:6.x
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
- <version>5.x.x</version>
+ <version>6.0.0</version>
</dependency>| Removed | Replacement | Notes |
|---|---|---|
RxSession / RxTransaction | Use async AsyncSession or sync Session | Reactive API removed |
Bookmark multi-value constructor | Bookmark.from(iterable) | |
session.readTransaction(fn) | session.executeRead(fn) | |
session.writeTransaction(fn) | session.executeWrite(fn) | |
TrustStrategy.certFile() | TrustStrategy.trustCustomCertificateSignedBy(path) | |
Notification.severity() | Notification.severityLevel() | Returns typed enum |
neo4j-java-driver-bom no longer imports netty-bom as of 6.0.1. If using Netty native transport, add explicit netty-bom import.
Legacy Logging API deprecated → configure System.Logger instead.
Full changelog: references/java-driver.md
Min requirements: .NET 8, 9, or 10 (.NET Standard 2.1 dropped); NuGet Neo4j.Driver 6.x
- <PackageReference Include="Neo4j.Driver" Version="5.*" />
+ <PackageReference Include="Neo4j.Driver" Version="6.*" />Neo4j.Driver.Signed no longer receives updates — switch to Neo4j.Driver (now signed).
| Removed | Replacement | Notes |
|---|---|---|
ILogger | INeo4jLogger | Interface renamed |
ConfigBuilder.WithIpv6Enabled() | Remove call — IPv6 always enabled | |
Config.Ipv6Enabled property | Remove — always true | |
| Simple driver | Use standard IDriver | Removed entirely |
Full changelog: references/dotnet-driver.md
Min requirements: Go 1.24; module github.com/neo4j/neo4j-go-driver/v6
- require github.com/neo4j/neo4j-go-driver/v5 v5.x.x
+ require github.com/neo4j/neo4j-go-driver/v6 v6.x.xUpdate all imports from /v5/ to /v6/.
| Old | New | Notes |
|---|---|---|
neo4j.NewDriver(...) | neo4j.NewDriverWithContext(...) | Context-aware; or just use neo4j.NewDriver (now has context) |
neo4j.Config struct | config.Config | Import github.com/neo4j/neo4j-go-driver/v6/neo4j/config |
neo4j.ServerAddressResolver | config.ServerAddressResolver | |
neo4j.LogLevel / neo4j.ERROR etc. | log.Level / log.ERROR | Import neo4j/log sub-package |
log.Console | log.ToConsole | |
log.Void | log.ToVoid | |
neo4j.Single[T] | neo4j.SingleT[T] (still present; context variant preferred) | |
neo4j.Driver interface | neo4j.DriverWithContext | *WithContext variants are now primary |
neo4j.Session interface | neo4j.SessionWithContext | |
Config.RootCAs field | Config.TlsConfig | Pass *tls.Config directly |
| Notification constants (old pkg) | notifications.* equivalents | Import neo4j/notifications |
In v6, neo4j.NewDriverWithContext is the canonical constructor. The old *WithContext method names are deprecated (targeted for removal in v7) — base names now include context by default.
- driver, err := neo4j.NewDriver(uri, neo4j.BasicAuth(user, pass, ""))
+ driver, err := neo4j.NewDriverWithContext(uri, neo4j.BasicAuth(user, pass, ""))Full migration guide: references/go-driver.md
| Neo4j version | Cypher dialect | Driver 6.x compatible | Notes |
|---|---|---|---|
| 4.4 LTS | Cypher 4 | Yes (bolt compat) | Support ended Nov 2025 |
| 5.26 LTS | Cypher 5 | Yes | Supported until Nov 2028 |
| 2025.01–2025.05 | Cypher 5 | Yes | CalVer era begins |
| 2025.06+ | Cypher 5 or 25 | Yes | Cypher 25 new default |
| 2026.01+ | Cypher 25 | Yes | SEARCH clause available |
| 2026.07.0 | Cypher 25 | Yes | Skip this patch — block-format UTF-8 regression makes trim() fail queries and corrupt stored strings; upgrade target is 2026.07.1 |
| 2026.08+ | Cypher 25 | Yes | UUID type + string interpolation added; UUID properties require driver >= 6.2 (Python >= 6.3) — older drivers return placeholder MAP and warn 03N95; 2026.08 bundles Java driver 6.2.1; skip 2026.08.0 (query-failure bug) — target 2026.08.1+ |
| 2026.09+ | Cypher 25 | Yes | SEARCH clause accepts FULLTEXT INDEX; page cache reserves full server.memory.pagecache.size at startup |
| Aura after 2026.12 LTS | Cypher 25 | Yes | dbms.components() switches from fixed 5.27 to calendar version (e.g. 2027.01) — fix code (incl. Kafka/Spark/GraphQL integrations) that parses a 5.x string |
| Aura 2027.01 | Cypher 25 | Yes | Native-user password minimum 8 → 16 chars on create/change |
Store format: no changes between 4.4 and 2026.x. block format default for new Enterprise dbs since 5.22. high_limit and standard deprecated in 5.23, removed after 2026 LTS — Neo4j refuses to start on a high_limit store after that LTS; run offline neo4j-admin database migrate --to-format=block <db> on or before the LTS.
Server JVM: 2026.x runs on Java SE 21 or 25; Java 21 support ends with the 2026 LTS — move servers to Java 25 [2026.08 deprecation].
No downgrades supported — take a backup before any server upgrade.
# Python: run with dev mode to surface all deprecation warnings
python -W error::DeprecationWarning -m pytest tests/
# JS: run tests
npm test
# Java
mvn test
# .NET
dotnet test
# Go
go test ./...After test run: if any DeprecationWarning or deprecated-API log appears, treat as ERROR — fix before proceeding.
For Cypher: run each migrated query through EXPLAIN first:
# Via Query API v2
curl -X POST https://<host>/db/<database>/query/v2 \
-u <user>:<password> -H "Content-Type: application/json" \
-d '{"statement": "EXPLAIN <your query>"}'| Error | Cause | Fix |
|---|---|---|
id(n) returns string, int comparison fails | elementId() returns string | Update stored IDs to string; fix WHERE clauses |
Cannot merge node using null property value | MERGE key resolved to null | Validate params before MERGE |
Neo4j.Driver.Exceptions.AuthenticationException | Wrong credential config after .NET rename | Update INeo4jLogger usage; verify config |
TypeError: neo4j-driver is deprecated | Old package installed | Replace with neo4j>=6.0.0 in requirements |
AttributeError: 'Session' has no 'read_transaction' | Python 5→6, method removed | Replace with execute_read() |
RxSession not found | Java 6.x, Reactive API removed | Migrate to async or sync session |
undefined is not a function (.readTransaction) | JS 6.x, method removed | Replace with executeRead() |
Go: undefined: neo4j.Config | v5 import path still present | Update all imports to /v6/ |
PERIODIC COMMIT not supported | Cypher 25, clause removed | Use CALL (...) IN TRANSACTIONS |
Unknown function id() | id() removed in Cypher 25 | Replace with elementId() |
Load on demand — high discovery rate because explicitly linked:
For any syntax not covered above, fetch:
https://neo4j.com/docs/cypher-manual/25/deprecations-additions-removals-compatibility/
id(n) → elementId(n) and downstream int→string conversions fixedPERIODIC COMMIT → CALL IN TRANSACTIONS in all batch writesCALL { WITH x ... } → CALL (x) { ... } in all subqueriesneo4j-driver → neo4j)read_transaction/readTransaction → execute_read/executeRead).close())last_bookmark → last_bookmarks, Bookmark → Bookmarks)/v5/ to /v6/; neo4j.Config → config.ConfigILogger → INeo4jLogger; Neo4j.Driver.Signed → Neo4j.DriverEXPLAIN run on all migrated Cypher queries; no errorsneo4j-contrib/neo4j-skills
39247b9
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.