Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions java-manual/modules/ROOT/pages/data-types.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -461,17 +461,6 @@ public class App {
For full documentation, see link:https://neo4j.com/docs/api/java-driver/{java-driver-version}/org.neo4j.driver/org/neo4j/driver/internal/value/PathValue.html[API documentation -> PathValue].


== Exceptions

The driver can raise a number of different exceptions, see link:https://neo4j.com/docs/api/java-driver/{java-driver-version}/org.neo4j.driver/org/neo4j/driver/exceptions/package-summary.html[API documentation -> Exceptions].
For a list of errors the server can return, see link:{neo4j-docs-base-uri}/status-codes/{page-version}[Status codes].

Some server errors are marked as safe to retry without need to alter the original request.
Examples of such errors are deadlocks, memory issues, or connectivity issues.
Driver's exceptions implementing link:https://neo4j.com/docs/api/java-driver/{java-driver-version}/org.neo4j.driver/org/neo4j/driver/exceptions/RetryableException.html[`RetryableException`] are such that a further attempt at the operation that caused it might be successful.
This is particular useful when running queries in xref:transactions#explicit-transactions[explicit transactions], to know if a failed query is worth re-running.


ifndef::backend-pdf[]
[discrete.glossary]
== Glossary
Expand Down
116 changes: 105 additions & 11 deletions java-manual/modules/ROOT/pages/query-simple.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,104 @@ There can be circumstances where your query structure prevents the usage of para
For those rare use cases, see xref:query-advanced#_dynamic_values_in_property_keys_relationship_types_and_labels[Dynamic values in property keys, relationship types, and labels].


[#error-handling]
== Error handling

A query run may fail for a number of reasons, with different link:https://neo4j.com/docs/api/java-driver/{java-driver-version}/org.neo4j.driver/org/neo4j/driver/exceptions/package-summary.html[exceptions] being raised.
Some of them are due to Cypher syntax errors, permission issues, or other forms of misconfiguration/misusage.
The choice of how to handle those exceptions is up to your application: whether you want to be defensive (for example check if there are records to process to avoid link:https://neo4j.com/docs/api/java-driver/{java-driver-version}/org.neo4j.driver/org/neo4j/driver/exceptions/NoSuchRecordException.html[NoSuchRecordException]), or whether you want to catch and handle the exceptions as they arise.

[TIP]
The driver automatically retries to run a failed query, if the failure is deemed to be transient (for example due to temporary server unavailability).
When using `Driver.executableQuery()`, the driver automatically retries to run a failed query if the failure is deemed to be transient (for example due to temporary server unavailability).
An exception will be raised if the operation keeps failing after a number of attempts.

All exceptions coming from the server are subclasses of link:https://neo4j.com/docs/api/java-driver/{java-driver-version}/org.neo4j.driver/org/neo4j/driver/exceptions/Neo4jException.html[`Neo4jException`].
You can use an exception's code (retrievable with `.code()`) to stably identify a specific error; error messages are instead not stable markers, and should not be relied upon.

.Basic error handling
[source, java, role=nocollapse]
----
// import org.neo4j.driver.exceptions.Neo4jException;

try {
var result = driver.executableQuery("MATCH (p:Person) RETURN ")
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute();
} catch (Neo4jException e) {
System.out.printf("Neo4j error code: %s\n", e.code());
System.out.printf("Exception message: %s\n", e.getMessage());
}
/*
Neo4j error code: Neo.ClientError.Statement.SyntaxError
Exception message: Invalid input '': expected an expression, '*', 'ALL' or 'DISTINCT' (line 1, column 24 (offset: 23))
"MATCH (p:Person) RETURN"
^
*/
----

Exception objects also expose errors as GQL-status objects.
The main difference between link:https://neo4j.com/docs/status-codes/current/errors/all-errors/[Neo4j error codes] and link:https://neo4j.com/docs/status-codes/current/errors/gql-errors/[GQL error codes] is that the GQL ones are more granular: a single Neo4j error code might be broken in several, more specific GQL error codes.

The actual _cause_ that triggered an exception is sometimes found in the optional GQL-status object retrievable with `.gqlCause()`, which is itself a `Neo4jException`.
You might need to recursively traverse the cause chain before reaching the root cause of the exception you caught.
In the example below, the exception's GQL status code is `42001`, but the actual source of the error has status code `42I06`.

.Usage of `Neo4jException` with GQL-related methods
[source, java]
----
// import org.neo4j.driver.exceptions.Neo4jException;

try {
var result = driver.executableQuery("MATCH (p:Person) RETURN ")
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute();
} catch (Neo4jException e) {
System.out.printf("Exception GQL status code: %s\n", e.gqlStatus());
System.out.printf("Exception GQL status description: %s\n", e.statusDescription());
System.out.printf("Exception GQL classification: %s\n", e.rawClassification());
System.out.printf("Exception GQL cause: %s\n", e.gqlCause());
System.out.printf("Exception GQL diagnostic record: %s\n", e.diagnosticRecord());
}
/*
Exception GQL status code: 42001
Exception GQL status description: error: syntax error or access rule violation - invalid syntax
Exception GQL classification: Optional[CLIENT_ERROR]
Exception GQL cause: Optional[org.neo4j.driver.exceptions.Neo4jException: 42I06: Invalid input '', expected: an expression, '*', 'ALL' or 'DISTINCT'.]
Exception GQL diagnostic record: {_classification="CLIENT_ERROR", OPERATION_CODE="0", OPERATION="", CURRENT_SCHEMA="/", _position={column: 24, offset: 23, line: 1}}
*/
----

GQL status codes are particularly helpful when you want your application to behave differently depending on the exact error that was raised by the server.

.Distinguishing between different error codes
[source, java]
----
// import org.neo4j.driver.exceptions.Neo4jException;

try {
var result = driver.executableQuery("CREATE (p:Person {name: $name}) RETURN ")
.withParameters(Map.of("name", "Frida"))
.withConfig(QueryConfig.builder().withDatabase("neo4j").build())
.execute();
} catch (Neo4jException e) {
if (e.containsGqlStatus("42001")) {
// Neo.ClientError.Statement.SyntaxError
// special handling of syntax error in query
System.out.println(e.findByGqlStatus("42001").get().getMessage());
} else if (e.containsGqlStatus("42NFF")) {
// Neo.ClientError.Security.Forbidden
// special handling of user not having CREATE permissions
System.out.println(e.findByGqlStatus("42NFF").get().getMessage());
} else {
// handling of all other exceptions
System.out.println(e.getMessage());
}
}
----

[NOTE]
====
The GQL status code `50N42` is returned when an error does not have a GQL-status object.
This can happen if the driver is connected to an older Neo4j server.
Don't rely on this status code, as future Neo4j server versions might change it with a more appropriate one.
====


== Query configuration

Expand Down Expand Up @@ -294,13 +382,15 @@ import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.Record;
import org.neo4j.driver.QueryConfig;
import org.neo4j.driver.RoutingControl;
import org.neo4j.driver.exceptions.Neo4jException;

public class App {

private static final String dbUri = "<database-uri>";
private static final String dbUser = "<username>";
private static final String dbPassword = "<password>";

public static void main(String... args) {
final String dbUri = "<database-uri>";
final String dbUser = "<username>";
final String dbPassword = "<password>";

try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword))) {

Expand Down Expand Up @@ -359,9 +449,13 @@ public class App {
result.summary().query(), result.records().size(),
result.summary().resultAvailableAfter(TimeUnit.MILLISECONDS));

} catch (Exception e) {
System.out.println(e.getMessage());
System.exit(1);
} catch (Neo4jException e) {
if (e.gqlStatus().equals("42NFF")) {
System.out.println("There was a permission issue. Make sure you have correct permissions and try again.");
} else {
System.out.println(e.getMessage());
System.exit(1);
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions java-manual/modules/ROOT/pages/transactions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ try (var session = driver.session(SessionConfig.builder().withDatabase("neo4j").
}
----

[TIP]
====
Queries run with `tx.run()` failing due to a transient server error can be retried without need to alter the original request.
Exceptions implementing link:https://neo4j.com/docs/api/java-driver/{java-driver-version}/org.neo4j.driver/org/neo4j/driver/exceptions/RetryableException.html[`RetryableException`] are such that a further attempt at the operation that caused it might be successful.
====

An explicit transaction can be committed with link:https://neo4j.com/docs/api/java-driver/{java-driver-version}/org.neo4j.driver/org/neo4j/driver/Transaction.html#commit()[`Transaction.commit()`] or rolled back with link:https://neo4j.com/docs/api/java-driver/{java-driver-version}/org.neo4j.driver/org/neo4j/driver/Transaction.html#rollback()[`Transaction.rollback()`].
If no explicit action is taken, the driver will automatically roll back the transaction at the end of its lifetime.

Expand Down