scylla_cql/frame/
frame_errors.rs

1//! Low-level errors that can occur during CQL frame parsing and serialization.
2
3use std::error::Error;
4use std::sync::Arc;
5
6pub use super::request::{
7    auth_response::AuthResponseSerializationError,
8    batch::{BatchSerializationError, BatchStatementSerializationError},
9    execute::ExecuteSerializationError,
10    prepare::PrepareSerializationError,
11    query::{QueryParametersSerializationError, QuerySerializationError},
12    register::RegisterSerializationError,
13    startup::StartupSerializationError,
14};
15
16use super::response::CqlResponseKind;
17use super::TryFromPrimitiveError;
18use crate::utils::parse::ParseErrorCause;
19use thiserror::Error;
20
21/// An error returned by `parse_response_body_extensions`.
22///
23/// It represents an error that occurred during deserialization of
24/// frame body extensions. These extensions include tracing id,
25/// warnings and custom payload.
26///
27/// Possible error kinds:
28/// - failed to decompress frame body (decompression is required for further deserialization)
29/// - failed to deserialize tracing id (body ext.)
30/// - failed to deserialize warnings list (body ext.)
31/// - failed to deserialize custom payload map (body ext.)
32#[derive(Error, Debug, Clone)]
33#[non_exhaustive]
34pub enum FrameBodyExtensionsParseError {
35    /// Frame is compressed, but no compression was negotiated for the connection.
36    #[error("Frame is compressed, but no compression negotiated for connection.")]
37    NoCompressionNegotiated,
38
39    /// Failed to deserialize frame trace id.
40    #[error("Malformed trace id: {0}")]
41    TraceIdParse(LowLevelDeserializationError),
42
43    /// Failed to deserialize warnings attached to frame.
44    #[error("Malformed warnings list: {0}")]
45    WarningsListParse(LowLevelDeserializationError),
46
47    /// Failed to deserialize frame's custom payload.
48    #[error("Malformed custom payload map: {0}")]
49    CustomPayloadMapParse(LowLevelDeserializationError),
50
51    /// Failed to decompress frame body (snap).
52    #[error("Snap decompression error: {0}")]
53    SnapDecompressError(Arc<dyn Error + Sync + Send>),
54
55    /// Failed to decompress frame body (lz4).
56    #[error("Error decompressing lz4 data {0}")]
57    Lz4DecompressError(Arc<dyn Error + Sync + Send>),
58}
59
60/// An error that occurred during frame header deserialization.
61#[derive(Debug, Error)]
62#[non_exhaustive]
63pub enum FrameHeaderParseError {
64    /// Failed to read the frame header from the socket.
65    #[error("Failed to read the frame header: {0}")]
66    HeaderIoError(std::io::Error),
67
68    /// Received a frame marked as coming from a client.
69    #[error("Received frame marked as coming from a client")]
70    FrameFromClient,
71
72    /// Received a frame marked as coming from a server, while expecting a frame
73    /// coming from a client.
74    // FIXME: this should not belong here. User always expects a frame from server.
75    // This variant is only used in scylla-proxy - need to investigate it later.
76    #[error("Received frame marked as coming from the server")]
77    FrameFromServer,
78
79    /// Received a frame with unsupported version.
80    #[error("Received a frame from version {0}, but only 4 is supported")]
81    VersionNotSupported(u8),
82
83    /// Received unknown response opcode.
84    #[error("Unrecognized response opcode {0}")]
85    UnknownResponseOpcode(#[from] TryFromPrimitiveError<u8>),
86
87    /// Failed to read frame body from the socket.
88    #[error("Failed to read a chunk of response body. Expected {0} more bytes, error: {1}")]
89    BodyChunkIoError(usize, std::io::Error),
90
91    /// Connection was closed before whole frame was read.
92    #[error("Connection was closed before body was read: missing {0} out of {1}")]
93    ConnectionClosed(usize, usize),
94}
95
96/// An error that occurred during CQL request serialization.
97#[non_exhaustive]
98#[derive(Error, Debug, Clone)]
99pub enum CqlRequestSerializationError {
100    /// Failed to serialize STARTUP request.
101    #[error("Failed to serialize STARTUP request: {0}")]
102    StartupSerialization(#[from] StartupSerializationError),
103
104    /// Failed to serialize REGISTER request.
105    #[error("Failed to serialize REGISTER request: {0}")]
106    RegisterSerialization(#[from] RegisterSerializationError),
107
108    /// Failed to serialize AUTH_RESPONSE request.
109    #[error("Failed to serialize AUTH_RESPONSE request: {0}")]
110    AuthResponseSerialization(#[from] AuthResponseSerializationError),
111
112    /// Failed to serialize BATCH request.
113    #[error("Failed to serialize BATCH request: {0}")]
114    BatchSerialization(#[from] BatchSerializationError),
115
116    /// Failed to serialize PREPARE request.
117    #[error("Failed to serialize PREPARE request: {0}")]
118    PrepareSerialization(#[from] PrepareSerializationError),
119
120    /// Failed to serialize EXECUTE request.
121    #[error("Failed to serialize EXECUTE request: {0}")]
122    ExecuteSerialization(#[from] ExecuteSerializationError),
123
124    /// Failed to serialize QUERY request.
125    #[error("Failed to serialize QUERY request: {0}")]
126    QuerySerialization(#[from] QuerySerializationError),
127
128    /// Request body compression failed.
129    #[error("Snap compression error: {0}")]
130    SnapCompressError(Arc<dyn Error + Sync + Send>),
131}
132
133/// An error type returned when deserialization of CQL
134/// server response fails.
135#[non_exhaustive]
136#[derive(Error, Debug, Clone)]
137// Check triggers because all variants begin with "Cql".
138// TODO(2.0): Remove the "Cql" prefix from variants.
139#[expect(clippy::enum_variant_names)]
140pub enum CqlResponseParseError {
141    #[error("Failed to deserialize ERROR response: {0}")]
142    CqlErrorParseError(#[from] CqlErrorParseError),
143    #[error("Failed to deserialize AUTH_CHALLENGE response: {0}")]
144    CqlAuthChallengeParseError(#[from] CqlAuthChallengeParseError),
145    #[error("Failed to deserialize AUTH_SUCCESS response: {0}")]
146    CqlAuthSuccessParseError(#[from] CqlAuthSuccessParseError),
147    #[error("Failed to deserialize AUTHENTICATE response: {0}")]
148    CqlAuthenticateParseError(#[from] CqlAuthenticateParseError),
149    #[error("Failed to deserialize SUPPORTED response: {0}")]
150    CqlSupportedParseError(#[from] CqlSupportedParseError),
151    #[error("Failed to deserialize EVENT response: {0}")]
152    CqlEventParseError(#[from] CqlEventParseError),
153    #[error(transparent)]
154    CqlResultParseError(#[from] CqlResultParseError),
155}
156
157impl CqlResponseParseError {
158    /// Retrieves the kind of CQL response that this error corresponds to.
159    pub fn to_response_kind(&self) -> CqlResponseKind {
160        match self {
161            CqlResponseParseError::CqlErrorParseError(_) => CqlResponseKind::Error,
162            CqlResponseParseError::CqlAuthChallengeParseError(_) => CqlResponseKind::AuthChallenge,
163            CqlResponseParseError::CqlAuthSuccessParseError(_) => CqlResponseKind::AuthSuccess,
164            CqlResponseParseError::CqlAuthenticateParseError(_) => CqlResponseKind::Authenticate,
165            CqlResponseParseError::CqlSupportedParseError(_) => CqlResponseKind::Supported,
166            CqlResponseParseError::CqlEventParseError(_) => CqlResponseKind::Event,
167            CqlResponseParseError::CqlResultParseError(_) => CqlResponseKind::Result,
168        }
169    }
170}
171
172/// An error type returned when deserialization of ERROR response fails.
173#[non_exhaustive]
174#[derive(Error, Debug, Clone)]
175pub enum CqlErrorParseError {
176    #[error("Malformed error code: {0}")]
177    ErrorCodeParseError(LowLevelDeserializationError),
178    #[error("Malformed error reason: {0}")]
179    ReasonParseError(LowLevelDeserializationError),
180    #[error("Malformed error field {field} of DB error {db_error}: {err}")]
181    MalformedErrorField {
182        db_error: &'static str,
183        field: &'static str,
184        err: LowLevelDeserializationError,
185    },
186}
187
188/// An error type returned when deserialization of AUTH_CHALLENGE response fails.
189#[non_exhaustive]
190#[derive(Error, Debug, Clone)]
191pub enum CqlAuthChallengeParseError {
192    #[error("Malformed authenticate message: {0}")]
193    AuthMessageParseError(LowLevelDeserializationError),
194}
195
196/// An error type returned when deserialization of AUTH_SUCCESS response fails.
197#[non_exhaustive]
198#[derive(Error, Debug, Clone)]
199pub enum CqlAuthSuccessParseError {
200    #[error("Malformed success message: {0}")]
201    SuccessMessageParseError(LowLevelDeserializationError),
202}
203
204/// An error type returned when deserialization of AUTHENTICATE response fails.
205#[non_exhaustive]
206#[derive(Error, Debug, Clone)]
207pub enum CqlAuthenticateParseError {
208    #[error("Malformed authenticator name: {0}")]
209    AuthNameParseError(LowLevelDeserializationError),
210}
211
212/// An error type returned when deserialization of SUPPORTED response fails.
213#[non_exhaustive]
214#[derive(Error, Debug, Clone)]
215pub enum CqlSupportedParseError {
216    #[error("Malformed options map: {0}")]
217    OptionsMapDeserialization(LowLevelDeserializationError),
218}
219
220/// An error type returned when deserialization of RESULT response fails.
221#[non_exhaustive]
222#[derive(Error, Debug, Clone)]
223pub enum CqlResultParseError {
224    #[error("Malformed RESULT response id: {0}")]
225    ResultIdParseError(LowLevelDeserializationError),
226    #[error("Unknown RESULT response id: {0}")]
227    UnknownResultId(i32),
228    #[error("RESULT:Set_keyspace response deserialization failed: {0}")]
229    SetKeyspaceParseError(#[from] SetKeyspaceParseError),
230    // This is an error returned during deserialization of
231    // `RESULT::Schema_change` response, and not `EVENT` response.
232    #[error("RESULT:Schema_change response deserialization failed: {0}")]
233    SchemaChangeParseError(#[from] SchemaChangeEventParseError),
234    #[error("RESULT:Prepared response deserialization failed: {0}")]
235    PreparedParseError(#[from] PreparedParseError),
236    #[error("RESULT:Rows response deserialization failed: {0}")]
237    RawRowsParseError(#[from] RawRowsAndPagingStateResponseParseError),
238}
239
240#[non_exhaustive]
241#[derive(Error, Debug, Clone)]
242pub enum SetKeyspaceParseError {
243    #[error("Malformed keyspace name: {0}")]
244    MalformedKeyspaceName(#[from] LowLevelDeserializationError),
245}
246
247/// An error type returned when deserialization of
248/// `EVENT` response fails.
249#[non_exhaustive]
250#[derive(Error, Debug, Clone)]
251pub enum CqlEventParseError {
252    #[error("Malformed event type string: {0}")]
253    EventTypeParseError(LowLevelDeserializationError),
254    #[error("Unknown event type: {0}")]
255    UnknownEventType(String),
256    #[error("Failed to deserialize schema change event: {0}")]
257    SchemaChangeEventParseError(#[from] SchemaChangeEventParseError),
258    #[error("Failed to deserialize topology change event: {0}")]
259    TopologyChangeEventParseError(ClusterChangeEventParseError),
260    #[error("Failed to deserialize status change event: {0}")]
261    StatusChangeEventParseError(ClusterChangeEventParseError),
262}
263
264/// An error type returned when deserialization of
265/// SchemaChangeEvent fails.
266#[non_exhaustive]
267#[derive(Error, Debug, Clone)]
268pub enum SchemaChangeEventParseError {
269    #[error("Malformed schema change type string: {0}")]
270    TypeOfChangeParseError(LowLevelDeserializationError),
271    #[error("Malformed schema change target string:: {0}")]
272    TargetTypeParseError(LowLevelDeserializationError),
273    #[error("Malformed name of keyspace affected by schema change: {0}")]
274    AffectedKeyspaceParseError(LowLevelDeserializationError),
275    #[error("Malformed name of the table affected by schema change: {0}")]
276    AffectedTableNameParseError(LowLevelDeserializationError),
277    #[error("Malformed name of the target affected by schema change: {0}")]
278    AffectedTargetNameParseError(LowLevelDeserializationError),
279    #[error(
280        "Malformed number of arguments of the function/aggregate affected by schema change: {0}"
281    )]
282    ArgumentCountParseError(LowLevelDeserializationError),
283    #[error("Malformed argument of the function/aggregate affected by schema change: {0}")]
284    FunctionArgumentParseError(LowLevelDeserializationError),
285    #[error("Unknown target of schema change: {0}")]
286    UnknownTargetOfSchemaChange(String),
287}
288
289/// An error type returned when deserialization of [Status/Topology]ChangeEvent fails.
290#[non_exhaustive]
291#[derive(Error, Debug, Clone)]
292pub enum ClusterChangeEventParseError {
293    #[error("Malformed type of change: {0}")]
294    TypeOfChangeParseError(LowLevelDeserializationError),
295    #[error("Malformed node address: {0}")]
296    NodeAddressParseError(LowLevelDeserializationError),
297    #[error("Unknown type of change: {0}")]
298    UnknownTypeOfChange(String),
299}
300
301/// An error type returned when deserialization
302/// of `RESULT::`Prepared` response fails.
303#[non_exhaustive]
304#[derive(Debug, Error, Clone)]
305pub enum PreparedParseError {
306    #[error("Malformed prepared statement's id length: {0}")]
307    IdLengthParseError(LowLevelDeserializationError),
308    #[error("Invalid result metadata: {0}")]
309    ResultMetadataParseError(ResultMetadataParseError),
310    #[error("Invalid prepared metadata: {0}")]
311    PreparedMetadataParseError(PreparedMetadataParseError),
312    #[error("Non-zero paging state in result metadata: {0:?}")]
313    NonZeroPagingState(Arc<[u8]>),
314}
315
316/// An error that occurred during initial deserialization of
317/// `RESULT:Rows` response. Since the deserialization of rows is lazy,
318/// we initially only need to deserialize:
319/// - result metadata flags
320/// - column count (result metadata)
321/// - paging state response
322#[non_exhaustive]
323#[derive(Debug, Error, Clone)]
324// Check triggers because all variants end with "ParseError".
325// TODO(2.0): Remove the "ParseError" postfix from variants.
326#[expect(clippy::enum_variant_names)]
327pub enum RawRowsAndPagingStateResponseParseError {
328    /// Failed to parse metadata flags.
329    #[error("Malformed metadata flags: {0}")]
330    FlagsParseError(LowLevelDeserializationError),
331
332    /// Failed to parse column count.
333    #[error("Malformed column count: {0}")]
334    ColumnCountParseError(LowLevelDeserializationError),
335
336    /// Failed to parse paging state response.
337    #[error("Malformed paging state: {0}")]
338    PagingStateParseError(LowLevelDeserializationError),
339}
340
341/// An error type returned when deserialization
342/// of statement's prepared metadata failed.
343#[non_exhaustive]
344#[derive(Error, Debug, Clone)]
345// Check triggers because all variants end with "ParseError".
346// TODO(2.0): Remove the "ParseError" postfix from variants.
347#[expect(clippy::enum_variant_names)]
348pub enum PreparedMetadataParseError {
349    /// Failed to parse metadata flags.
350    #[error("Malformed metadata flags: {0}")]
351    FlagsParseError(LowLevelDeserializationError),
352
353    /// Failed to parse column count.
354    #[error("Malformed column count: {0}")]
355    ColumnCountParseError(LowLevelDeserializationError),
356
357    /// Failed to parse partition key count.
358    #[error("Malformed partition key count: {0}")]
359    PkCountParseError(LowLevelDeserializationError),
360
361    /// Failed to parse partition key index.
362    #[error("Malformed partition key index: {0}")]
363    PkIndexParseError(LowLevelDeserializationError),
364
365    /// Failed to parse global table spec.
366    #[error("Invalid global table spec: {0}")]
367    GlobalTableSpecParseError(#[from] TableSpecParseError),
368
369    /// Failed to parse column spec.
370    #[error("Invalid column spec: {0}")]
371    ColumnSpecParseError(#[from] ColumnSpecParseError),
372}
373
374/// An error returned when lazy deserialization of
375/// result metadata and rows count fails.
376#[non_exhaustive]
377#[derive(Error, Debug, Clone)]
378pub enum ResultMetadataAndRowsCountParseError {
379    /// Failed to deserialize result metadata.
380    #[error("Failed to lazily deserialize result metadata: {0}")]
381    ResultMetadataParseError(#[from] ResultMetadataParseError),
382
383    /// Received malformed rows count from the server.
384    #[error("Malformed rows count: {0}")]
385    RowsCountParseError(LowLevelDeserializationError),
386}
387
388/// An error type returned when deserialization
389/// of result metadata failed.
390#[non_exhaustive]
391#[derive(Error, Debug, Clone)]
392// Check triggers because all variants end with "ParseError".
393// TODO(2.0): Remove the "ParseError" postfix from variants.
394#[expect(clippy::enum_variant_names)]
395pub enum ResultMetadataParseError {
396    /// Failed to parse metadata flags.
397    #[error("Malformed metadata flags: {0}")]
398    FlagsParseError(LowLevelDeserializationError),
399
400    /// Failed to parse column count.
401    #[error("Malformed column count: {0}")]
402    ColumnCountParseError(LowLevelDeserializationError),
403
404    /// Failed to parse paging state response.
405    #[error("Malformed paging state: {0}")]
406    PagingStateParseError(LowLevelDeserializationError),
407
408    /// Failed to parse global table spec.
409    #[error("Invalid global table spec: {0}")]
410    GlobalTableSpecParseError(#[from] TableSpecParseError),
411
412    /// Failed to parse column spec.
413    #[error("Invalid column spec: {0}")]
414    ColumnSpecParseError(#[from] ColumnSpecParseError),
415}
416
417/// An error type returned when deserialization
418/// of table specification fails.
419#[non_exhaustive]
420#[derive(Error, Debug, Clone)]
421pub enum TableSpecParseError {
422    #[error("Malformed keyspace name: {0}")]
423    MalformedKeyspaceName(LowLevelDeserializationError),
424    #[error("Malformed table name: {0}")]
425    MalformedTableName(LowLevelDeserializationError),
426}
427
428/// An error type returned when deserialization
429/// of table column specifications fails.
430#[non_exhaustive]
431#[derive(Error, Debug, Clone)]
432#[error("Column spec deserialization failed, column index: {column_index}, error: {kind}")]
433pub struct ColumnSpecParseError {
434    pub column_index: usize,
435    pub kind: ColumnSpecParseErrorKind,
436}
437
438/// The type of error that appeared during deserialization
439/// of a column specification.
440#[non_exhaustive]
441#[derive(Error, Debug, Clone)]
442// Check triggers because all variants end with "ParseError".
443// TODO(2.0): Remove the "ParseError" postfix from variants.
444#[expect(clippy::enum_variant_names)]
445pub enum ColumnSpecParseErrorKind {
446    #[error("Invalid table spec: {0}")]
447    TableSpecParseError(#[from] TableSpecParseError),
448    #[error("Malformed column name: {0}")]
449    ColumnNameParseError(#[from] LowLevelDeserializationError),
450    #[error("Invalid column type: {0}")]
451    ColumnTypeParseError(#[from] CqlTypeParseError),
452}
453
454/// An error type returned when deserialization of Custom CQL type name fails.
455#[non_exhaustive]
456#[derive(Error, Debug, Clone, PartialEq, Eq)]
457pub enum CustomTypeParseError {
458    #[error("Malformed simple custom type name: {0}")]
459    UnknownSimpleCustomTypeName(String),
460    #[error("Malformed complex custom type name: {0}")]
461    UnknownComplexCustomTypeName(String),
462    #[error("Unexpected character encountered: {0}, expected: {1}")]
463    UnexpectedCharacter(char, char),
464    #[error("Unable to parse an integer: {0}")]
465    IntegerParseError(ParseErrorCause),
466    #[error("Unexpected end of input")]
467    UnexpectedEndOfInput,
468    #[error("Bad hex string: {0}")]
469    BadHexString(String),
470    #[error("Bytes {0:?} do not represent a valid UTF-8 string")]
471    InvalidUtf8(Vec<u8>),
472    #[error("Wrong number of parameters {actual}, expected: {expected}")]
473    InvalidParameterCount { actual: usize, expected: usize },
474}
475
476/// An error type returned when deserialization of CQL type name fails.
477#[non_exhaustive]
478#[derive(Error, Debug, Clone)]
479pub enum CqlTypeParseError {
480    #[error("Malformed type id: {0}")]
481    TypeIdParseError(LowLevelDeserializationError),
482    #[error("Malformed custom type name: {0}")]
483    CustomTypeNameParseError(LowLevelDeserializationError),
484    #[error("Unsupported custom type: {0}")]
485    CustomTypeUnsupported(String),
486    #[error("Malformed name of UDT keyspace: {0}")]
487    UdtKeyspaceNameParseError(LowLevelDeserializationError),
488    #[error("Malformed UDT name: {0}")]
489    UdtNameParseError(LowLevelDeserializationError),
490    #[error("Malformed UDT fields count: {0}")]
491    UdtFieldsCountParseError(LowLevelDeserializationError),
492    #[error("Malformed UDT's field name: {0}")]
493    UdtFieldNameParseError(LowLevelDeserializationError),
494    #[error("Malformed tuple length: {0}")]
495    TupleLengthParseError(LowLevelDeserializationError),
496    #[error("CQL Type not yet implemented, id: {0}")]
497    TypeNotImplemented(u16),
498    #[error("Failed to parse custom CQL type: {0}")]
499    CustomTypeParseError(CustomTypeParseError),
500}
501
502/// A low level deserialization error.
503///
504/// This type of error is returned when deserialization
505/// of some primitive value fails.
506///
507/// Possible error kinds:
508/// - generic io error - reading from buffer failed
509/// - out of range integer conversion
510/// - conversion errors - e.g. slice-to-array or primitive-to-enum
511/// - not enough bytes in the buffer to deserialize a value
512#[non_exhaustive]
513#[derive(Error, Debug, Clone)]
514pub enum LowLevelDeserializationError {
515    #[error(transparent)]
516    IoError(Arc<std::io::Error>),
517    #[error(transparent)]
518    TryFromIntError(#[from] std::num::TryFromIntError),
519    #[error(transparent)]
520    TryFromSliceError(#[from] std::array::TryFromSliceError),
521    #[error("Not enough bytes! expected: {expected}, received: {received}")]
522    TooFewBytesReceived { expected: usize, received: usize },
523    #[error("Invalid value length: {0}")]
524    InvalidValueLength(i32),
525    #[error("Unknown consistency: {0}")]
526    UnknownConsistency(#[from] TryFromPrimitiveError<u16>),
527    #[error("Invalid inet bytes length: {0}. Accepted lengths are 4 and 16 bytes.")]
528    InvalidInetLength(u8),
529    #[error("UTF8 deserialization failed: {0}")]
530    UTF8DeserializationError(#[from] std::str::Utf8Error),
531}
532
533impl From<std::io::Error> for LowLevelDeserializationError {
534    fn from(value: std::io::Error) -> Self {
535        Self::IoError(Arc::new(value))
536    }
537}