1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Map kglite's typed [`KgError`] / [`KgErrorCode`] onto Bolt FAILURE
//! status codes (`Neo.{Class}.{Category}.{Title}` strings).
//!
//! The same typed hierarchy backs the Python boundary
//! (`kglite.CypherSyntaxError`, `kglite.CypherTimeoutError`, etc.);
//! this module wires it to the Bolt wire so the neo4j Python driver
//! raises the matching driver-side exception class
//! (`CypherSyntaxError` instead of generic `ClientError`).
//!
//! ## Mapping table
//!
//! | `KgErrorCode` | Neo4j status code | Driver class |
//! |---------------------------|----------------------------------------------------|--------------------|
//! | `CypherSyntax` | `Neo.ClientError.Statement.SyntaxError` | CypherSyntaxError |
//! | `CypherTimeout` | `Neo.ClientError.Transaction.TransactionTimedOut` | ClientError |
//! | `CypherTypeMismatch` | `Neo.ClientError.Statement.TypeError` | ClientError |
//! | `CypherExecution` | `Neo.DatabaseError.Statement.ExecutionFailed` | DatabaseError |
//! | `Schema` | `Neo.ClientError.Schema.ConstraintValidationFailed`| ClientError |
//! | `Validation` / `Expr` | `Neo.ClientError.Statement.ArgumentError` | ClientError |
//! | `NodeNotFound` / `ConnectionNotFound` / `PropertyNotFound` | `Neo.ClientError.Statement.EntityNotFound` | ClientError |
//! | `InvalidArgument` | `Neo.ClientError.Statement.ArgumentError` | ClientError |
//! | `MissingArgument` | `Neo.ClientError.Statement.ParameterMissing` | ClientError |
//! | `FileNotFound` / `FileFormat` / `FileIo` | `Neo.DatabaseError.General.UnknownError` (server-side I/O — surface as DB error) | DatabaseError |
//! | `Internal` | `Neo.DatabaseError.General.UnknownError` | DatabaseError |
//!
//! Codes that don't have an exact Neo4j equivalent reuse the closest
//! ClientError-class fallback (matches what most Neo4j servers do for
//! their own unmapped extensions).
//!
//! ## The server's own policy refusals
//!
//! Not every FAILURE comes from a `KgError`: the backend refuses some requests
//! before the engine sees them, using `BoltError` variants directly (boltr's
//! `to_failure_metadata` owns those codes). Three classes are in use, and the
//! split is deliberate — a driver routes on the class, so a refusal a client
//! can fix must never arrive as a `DatabaseError`.
//!
//! | Refusal | `BoltError` | Neo4j status code | Driver class |
//! |----------------------------------------------------------------|--------------|------------------------------------------|--------------|
//! | Permission: `--readonly`, disk-mode `db.checkpoint()` | `Forbidden` | `Neo.ClientError.Security.Forbidden` | ClientError |
//! | Request shape: auto-commit mutation, `tx_timeout`, zoned params | `Session` / `Protocol` | `Neo.ClientError.Request.Invalid` | ClientError |
//! | Server fault: a commit the WAL rejected, an unreachable outcome | `Backend` | `Neo.DatabaseError.General.UnknownError` | DatabaseError |
use BoltError;
use KgError;
/// Map a [`KgError`] to a [`BoltError::Query`] with the right
/// `Neo.{Class}.{Category}.{Title}` code. boltr's
/// `BoltError::to_failure_metadata` passes the code+message through
/// to the wire FAILURE response, where the driver routes by class
/// prefix (ClientError vs DatabaseError vs TransientError).
///
/// The Neo4j status-code dispatch table itself lives on
/// [`kglite::api::KgErrorCode::neo4j_status_code`] (lifted from
/// this module in 2026-05-25 so any future Neo4j-wire-compatible
/// binding shares the canonical mapping). This wrapper just bolts
/// the code into the protocol-level `BoltError::Query` shape.