apache_avro/
error.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::{
19    schema::{Name, Schema, SchemaKind, UnionSchema},
20    types::{Value, ValueKind},
21};
22use std::{error::Error as _, fmt};
23
24/// Errors encounterd by Avro.
25///
26/// To inspect the details of the error use [`details`](Self::details) or [`into_details`](Self::into_details)
27/// to get a [`Details`] which contains more precise error information.
28///
29/// See [`Details`] for all possible errors.
30#[derive(thiserror::Error, Debug)]
31#[repr(transparent)]
32#[error(transparent)]
33pub struct Error {
34    details: Box<Details>,
35}
36
37impl Error {
38    pub fn new(details: Details) -> Self {
39        Self {
40            details: Box::new(details),
41        }
42    }
43
44    pub fn details(&self) -> &Details {
45        &self.details
46    }
47
48    pub fn into_details(self) -> Details {
49        *self.details
50    }
51}
52
53impl From<Details> for Error {
54    fn from(details: Details) -> Self {
55        Self::new(details)
56    }
57}
58
59impl serde::ser::Error for Error {
60    fn custom<T: fmt::Display>(msg: T) -> Self {
61        Self::new(<Details as serde::ser::Error>::custom(msg))
62    }
63}
64
65impl serde::de::Error for Error {
66    fn custom<T: fmt::Display>(msg: T) -> Self {
67        Self::new(<Details as serde::de::Error>::custom(msg))
68    }
69}
70
71#[derive(thiserror::Error)]
72pub enum Details {
73    #[error("Bad Snappy CRC32; expected {expected:x} but got {actual:x}")]
74    SnappyCrc32 { expected: u32, actual: u32 },
75
76    #[error("Invalid u8 for bool: {0}")]
77    BoolValue(u8),
78
79    #[error("Not a fixed value, required for decimal with fixed schema: {0:?}")]
80    FixedValue(Value),
81
82    #[error("Not a bytes value, required for decimal with bytes schema: {0:?}")]
83    BytesValue(Value),
84
85    #[error("Not a string value, required for uuid: {0:?}")]
86    GetUuidFromStringValue(Value),
87
88    #[error("Two schemas with the same fullname were given: {0:?}")]
89    NameCollision(String),
90
91    #[error("Not a fixed or bytes type, required for decimal schema, got: {0:?}")]
92    ResolveDecimalSchema(SchemaKind),
93
94    #[error("Invalid utf-8 string")]
95    ConvertToUtf8(#[source] std::string::FromUtf8Error),
96
97    #[error("Invalid utf-8 string")]
98    ConvertToUtf8Error(#[source] std::str::Utf8Error),
99
100    /// Describes errors happened while validating Avro data.
101    #[error("Value does not match schema")]
102    Validation,
103
104    /// Describes errors happened while validating Avro data.
105    #[error("Value {value:?} does not match schema {schema:?}: Reason: {reason}")]
106    ValidationWithReason {
107        value: Value,
108        schema: Schema,
109        reason: String,
110    },
111
112    #[error("Unable to allocate {desired} bytes (maximum allowed: {maximum})")]
113    MemoryAllocation { desired: usize, maximum: usize },
114
115    /// Describe a specific error happening with decimal representation
116    #[error(
117        "Number of bytes requested for decimal sign extension {requested} is less than the number of bytes needed to decode {needed}"
118    )]
119    SignExtend { requested: usize, needed: usize },
120
121    #[error("Failed to read boolean bytes: {0}")]
122    ReadBoolean(#[source] std::io::Error),
123
124    #[error("Failed to read bytes: {0}")]
125    ReadBytes(#[source] std::io::Error),
126
127    #[error("Failed to read string: {0}")]
128    ReadString(#[source] std::io::Error),
129
130    #[error("Failed to read double: {0}")]
131    ReadDouble(#[source] std::io::Error),
132
133    #[error("Failed to read float: {0}")]
134    ReadFloat(#[source] std::io::Error),
135
136    #[error("Failed to read duration: {0}")]
137    ReadDuration(#[source] std::io::Error),
138
139    #[error("Failed to read fixed number of bytes '{1}': : {0}")]
140    ReadFixed(#[source] std::io::Error, usize),
141
142    #[error("Failed to convert &str to UUID: {0}")]
143    ConvertStrToUuid(#[source] uuid::Error),
144
145    #[error("Failed to convert Fixed bytes to UUID. It must be exactly 16 bytes, got {0}")]
146    ConvertFixedToUuid(usize),
147
148    #[error("Failed to convert Fixed bytes to UUID: {0}")]
149    ConvertSliceToUuid(#[source] uuid::Error),
150
151    #[error("Map key is not a string; key type is {0:?}")]
152    MapKeyType(ValueKind),
153
154    #[error("Union index {index} out of bounds: {num_variants}")]
155    GetUnionVariant { index: i64, num_variants: usize },
156
157    #[deprecated(since = "0.20.0", note = "This error variant is not generated anymore")]
158    #[error("Enum symbol index out of bounds: {num_variants}")]
159    EnumSymbolIndex { index: usize, num_variants: usize },
160
161    #[error("Enum symbol not found {0}")]
162    GetEnumSymbol(String),
163
164    #[error("Unable to decode enum index")]
165    GetEnumUnknownIndexValue,
166
167    #[error("Scale {scale} is greater than precision {precision}")]
168    GetScaleAndPrecision { scale: usize, precision: usize },
169
170    #[error(
171        "Fixed type number of bytes {size} is not large enough to hold decimal values of precision {precision}"
172    )]
173    GetScaleWithFixedSize { size: usize, precision: usize },
174
175    #[error("Expected Value::Uuid, got: {0:?}")]
176    GetUuid(Value),
177
178    #[error("Expected Value::BigDecimal, got: {0:?}")]
179    GetBigDecimal(Value),
180
181    #[error("Fixed bytes of size 12 expected, got Fixed of size {0}")]
182    GetDecimalFixedBytes(usize),
183
184    #[error("Expected Value::Duration or Value::Fixed(12), got: {0:?}")]
185    ResolveDuration(Value),
186
187    #[error("Expected Value::Decimal, Value::Bytes or Value::Fixed, got: {0:?}")]
188    ResolveDecimal(Value),
189
190    #[error("Missing field in record: {0:?}")]
191    GetField(String),
192
193    #[error("Unable to convert to u8, got {0:?}")]
194    GetU8(Value),
195
196    #[error("Precision {precision} too small to hold decimal values with {num_bytes} bytes")]
197    ComparePrecisionAndSize { precision: usize, num_bytes: usize },
198
199    #[error("Cannot convert length to i32: {1}")]
200    ConvertLengthToI32(#[source] std::num::TryFromIntError, usize),
201
202    #[error("Expected Value::Date or Value::Int, got: {0:?}")]
203    GetDate(Value),
204
205    #[error("Expected Value::TimeMillis or Value::Int, got: {0:?}")]
206    GetTimeMillis(Value),
207
208    #[error("Expected Value::TimeMicros, Value::Long or Value::Int, got: {0:?}")]
209    GetTimeMicros(Value),
210
211    #[error("Expected Value::TimestampMillis, Value::Long or Value::Int, got: {0:?}")]
212    GetTimestampMillis(Value),
213
214    #[error("Expected Value::TimestampMicros, Value::Long or Value::Int, got: {0:?}")]
215    GetTimestampMicros(Value),
216
217    #[error("Expected Value::TimestampNanos, Value::Long or Value::Int, got: {0:?}")]
218    GetTimestampNanos(Value),
219
220    #[error("Expected Value::LocalTimestampMillis, Value::Long or Value::Int, got: {0:?}")]
221    GetLocalTimestampMillis(Value),
222
223    #[error("Expected Value::LocalTimestampMicros, Value::Long or Value::Int, got: {0:?}")]
224    GetLocalTimestampMicros(Value),
225
226    #[error("Expected Value::LocalTimestampNanos, Value::Long or Value::Int, got: {0:?}")]
227    GetLocalTimestampNanos(Value),
228
229    #[error("Expected Value::Null, got: {0:?}")]
230    GetNull(Value),
231
232    #[error("Expected Value::Boolean, got: {0:?}")]
233    GetBoolean(Value),
234
235    #[error("Expected Value::Int, got: {0:?}")]
236    GetInt(Value),
237
238    #[error("Expected Value::Long or Value::Int, got: {0:?}")]
239    GetLong(Value),
240
241    #[error(r#"Expected Value::Double, Value::Float, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: {0:?}"#)]
242    GetDouble(Value),
243
244    #[error(r#"Expected Value::Float, Value::Double, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: {0:?}"#)]
245    GetFloat(Value),
246
247    #[error("Expected Value::Bytes, got: {0:?}")]
248    GetBytes(Value),
249
250    #[error("Expected Value::String, Value::Bytes or Value::Fixed, got: {0:?}")]
251    GetString(Value),
252
253    #[error("Expected Value::Enum, got: {0:?}")]
254    GetEnum(Value),
255
256    #[error("Fixed size mismatch, expected: {size}, got: {n}")]
257    CompareFixedSizes { size: usize, n: usize },
258
259    #[error("String expected for fixed, got: {0:?}")]
260    GetStringForFixed(Value),
261
262    #[error("Enum default {symbol:?} is not among allowed symbols {symbols:?}")]
263    GetEnumDefault {
264        symbol: String,
265        symbols: Vec<String>,
266    },
267
268    #[error("Enum value index {index} is out of bounds {nsymbols}")]
269    GetEnumValue { index: usize, nsymbols: usize },
270
271    #[error("Key {0} not found in decimal metadata JSON")]
272    GetDecimalMetadataFromJson(&'static str),
273
274    #[error("Could not find matching type in {schema:?} for {value:?}")]
275    FindUnionVariant { schema: UnionSchema, value: Value },
276
277    #[error("Union type should not be empty")]
278    EmptyUnion,
279
280    #[error("Array({expected:?}) expected, got {other:?}")]
281    GetArray { expected: SchemaKind, other: Value },
282
283    #[error("Map({expected:?}) expected, got {other:?}")]
284    GetMap { expected: SchemaKind, other: Value },
285
286    #[error("Record with fields {expected:?} expected, got {other:?}")]
287    GetRecord {
288        expected: Vec<(String, SchemaKind)>,
289        other: Value,
290    },
291
292    #[error("No `name` field")]
293    GetNameField,
294
295    #[error("No `name` in record field")]
296    GetNameFieldFromRecord,
297
298    #[error("Unions may not directly contain a union")]
299    GetNestedUnion,
300
301    #[error("Unions cannot contain duplicate types")]
302    GetUnionDuplicate,
303
304    #[error("One union type {0:?} must match the `default`'s value type {1:?}")]
305    GetDefaultUnion(SchemaKind, ValueKind),
306
307    #[error("`default`'s value type of field {0:?} in {1:?} must be {2:?}")]
308    GetDefaultRecordField(String, String, String),
309
310    #[error("JSON value {0} claims to be u64 but cannot be converted")]
311    GetU64FromJson(serde_json::Number),
312
313    #[error("JSON value {0} claims to be i64 but cannot be converted")]
314    GetI64FromJson(serde_json::Number),
315
316    #[error("Cannot convert u64 to usize: {1}")]
317    ConvertU64ToUsize(#[source] std::num::TryFromIntError, u64),
318
319    #[deprecated(since = "0.20.0", note = "This error variant is not generated anymore")]
320    #[error("Cannot convert u32 to usize: {1}")]
321    ConvertU32ToUsize(#[source] std::num::TryFromIntError, u32),
322
323    #[error("Cannot convert i64 to usize: {1}")]
324    ConvertI64ToUsize(#[source] std::num::TryFromIntError, i64),
325
326    #[error("Cannot convert i32 to usize: {1}")]
327    ConvertI32ToUsize(#[source] std::num::TryFromIntError, i32),
328
329    #[error("Invalid JSON value for decimal precision/scale integer: {0}")]
330    GetPrecisionOrScaleFromJson(serde_json::Number),
331
332    #[error("Failed to parse schema from JSON")]
333    ParseSchemaJson(#[source] serde_json::Error),
334
335    #[error("Failed to read schema")]
336    ReadSchemaFromReader(#[source] std::io::Error),
337
338    #[error("Must be a JSON string, object or array")]
339    ParseSchemaFromValidJson,
340
341    #[error("Unknown primitive type: {0}")]
342    ParsePrimitive(String),
343
344    #[error("invalid JSON for {key:?}: {value:?}")]
345    GetDecimalMetadataValueFromJson {
346        key: String,
347        value: serde_json::Value,
348    },
349
350    #[error("The decimal precision ({precision}) must be bigger or equal to the scale ({scale})")]
351    DecimalPrecisionLessThanScale { precision: usize, scale: usize },
352
353    #[error("The decimal precision ({precision}) must be a positive number")]
354    DecimalPrecisionMuBePositive { precision: usize },
355
356    #[deprecated(since = "0.20.0", note = "This error variant is not generated anymore")]
357    #[error("Unreadable big decimal sign")]
358    BigDecimalSign,
359
360    #[error("Unreadable length for big decimal inner bytes: {0}")]
361    BigDecimalLen(#[source] Box<Error>),
362
363    #[error("Unreadable big decimal scale")]
364    BigDecimalScale,
365
366    #[deprecated(since = "0.20.0", note = "This error variant is not generated anymore")]
367    #[error("Unexpected `type` {0} variant for `logicalType`")]
368    GetLogicalTypeVariant(serde_json::Value),
369
370    #[error("No `type` field found for `logicalType`")]
371    GetLogicalTypeField,
372
373    #[error("logicalType must be a string, but is {0:?}")]
374    GetLogicalTypeFieldType(serde_json::Value),
375
376    #[error("Unknown complex type: {0}")]
377    GetComplexType(serde_json::Value),
378
379    #[error("No `type` in complex type")]
380    GetComplexTypeField,
381
382    #[error("No `fields` in record")]
383    GetRecordFieldsJson,
384
385    #[error("No `symbols` field in enum")]
386    GetEnumSymbolsField,
387
388    #[error("Unable to parse `symbols` in enum")]
389    GetEnumSymbols,
390
391    #[error("Invalid enum symbol name {0}")]
392    EnumSymbolName(String),
393
394    #[error("Invalid field name {0}")]
395    FieldName(String),
396
397    #[error("Duplicate field name {0}")]
398    FieldNameDuplicate(String),
399
400    #[error("Invalid schema name {0}. It must match the regex '{1}'")]
401    InvalidSchemaName(String, &'static str),
402
403    #[error("Invalid namespace {0}. It must match the regex '{1}'")]
404    InvalidNamespace(String, &'static str),
405
406    #[error(
407        "Invalid schema: There is no type called '{0}', if you meant to define a non-primitive schema, it should be defined inside `type` attribute. Please review the specification"
408    )]
409    InvalidSchemaRecord(String),
410
411    #[error("Duplicate enum symbol {0}")]
412    EnumSymbolDuplicate(String),
413
414    #[error("Default value for enum must be a string! Got: {0}")]
415    EnumDefaultWrongType(serde_json::Value),
416
417    #[error("No `items` in array")]
418    GetArrayItemsField,
419
420    #[error("No `values` in map")]
421    GetMapValuesField,
422
423    #[error("Fixed schema `size` value must be a positive integer: {0}")]
424    GetFixedSizeFieldPositive(serde_json::Value),
425
426    #[error("Fixed schema has no `size`")]
427    GetFixedSizeField,
428
429    #[error("Fixed schema's default value length ({0}) does not match its size ({1})")]
430    FixedDefaultLenSizeMismatch(usize, u64),
431
432    #[deprecated(since = "0.20.0", note = "This error variant is not generated anymore")]
433    #[error("Failed to compress with flate: {0}")]
434    DeflateCompress(#[source] std::io::Error),
435
436    // no longer possible after migration from libflate to miniz_oxide
437    #[deprecated(since = "0.19.0", note = "This error can no longer occur")]
438    #[error("Failed to finish flate compressor: {0}")]
439    DeflateCompressFinish(#[source] std::io::Error),
440
441    #[error("Failed to decompress with flate: {0}")]
442    DeflateDecompress(#[source] std::io::Error),
443
444    #[cfg(feature = "snappy")]
445    #[error("Failed to compress with snappy: {0}")]
446    SnappyCompress(#[source] snap::Error),
447
448    #[cfg(feature = "snappy")]
449    #[error("Failed to get snappy decompression length: {0}")]
450    GetSnappyDecompressLen(#[source] snap::Error),
451
452    #[cfg(feature = "snappy")]
453    #[error("Failed to decompress with snappy: {0}")]
454    SnappyDecompress(#[source] snap::Error),
455
456    #[error("Failed to compress with zstd: {0}")]
457    ZstdCompress(#[source] std::io::Error),
458
459    #[error("Failed to decompress with zstd: {0}")]
460    ZstdDecompress(#[source] std::io::Error),
461
462    #[error("Failed to read header: {0}")]
463    ReadHeader(#[source] std::io::Error),
464
465    #[error("wrong magic in header")]
466    HeaderMagic,
467
468    #[error("Message Header mismatch. Expected: {0:?}. Actual: {1:?}")]
469    SingleObjectHeaderMismatch(Vec<u8>, Vec<u8>),
470
471    #[error("Failed to get JSON from avro.schema key in map")]
472    GetAvroSchemaFromMap,
473
474    #[error("no metadata in header")]
475    GetHeaderMetadata,
476
477    #[error("Failed to read marker bytes: {0}")]
478    ReadMarker(#[source] std::io::Error),
479
480    #[error("Failed to read block marker bytes: {0}")]
481    ReadBlockMarker(#[source] std::io::Error),
482
483    #[error("Read into buffer failed: {0}")]
484    ReadIntoBuf(#[source] std::io::Error),
485
486    #[error(
487        "Invalid sync marker! The sync marker in the data block \
488        doesn't match the file header's sync marker. This likely \
489        indicates data corruption, truncated file, or incorrectly \
490        concatenated Avro files. Verify file integrity and ensure \
491        proper file transmission or creation."
492    )]
493    GetBlockMarker,
494
495    #[error("Overflow when decoding integer value")]
496    IntegerOverflow,
497
498    #[error("Failed to read bytes for decoding variable length integer: {0}")]
499    ReadVariableIntegerBytes(#[source] std::io::Error),
500
501    #[error("Decoded integer out of range for i32: {1}: {0}")]
502    ZagI32(#[source] std::num::TryFromIntError, i64),
503
504    #[error("unable to read block")]
505    ReadBlock,
506
507    #[error("Failed to serialize value into Avro value: {0}")]
508    SerializeValue(String),
509
510    #[error("Failed to serialize value of type {value_type} using schema {schema:?}: {value}")]
511    SerializeValueWithSchema {
512        value_type: &'static str,
513        value: String,
514        schema: Schema,
515    },
516
517    #[error("Failed to serialize field '{field_name}' for record {record_schema:?}: {error}")]
518    SerializeRecordFieldWithSchema {
519        field_name: &'static str,
520        record_schema: Schema,
521        error: Box<Error>,
522    },
523
524    #[error("Failed to deserialize Avro value into value: {0}")]
525    DeserializeValue(String),
526
527    #[error("Failed to write buffer bytes during flush: {0}")]
528    WriteBytes(#[source] std::io::Error),
529
530    #[error("Failed to flush inner writer during flush: {0}")]
531    FlushWriter(#[source] std::io::Error),
532
533    #[error("Failed to write marker: {0}")]
534    WriteMarker(#[source] std::io::Error),
535
536    #[error("Failed to convert JSON to string: {0}")]
537    ConvertJsonToString(#[source] serde_json::Error),
538
539    /// Error while converting float to json value
540    #[error("failed to convert avro float to json: {0}")]
541    ConvertF64ToJson(f64),
542
543    /// Error while resolving Schema::Ref
544    #[error("Unresolved schema reference: {0}")]
545    SchemaResolutionError(Name),
546
547    #[error("The file metadata is already flushed.")]
548    FileHeaderAlreadyWritten,
549
550    #[error("Metadata keys starting with 'avro.' are reserved for internal usage: {0}.")]
551    InvalidMetadataKey(String),
552
553    /// Error when two named schema have the same fully qualified name
554    #[error("Two named schema defined for same fullname: {0}.")]
555    AmbiguousSchemaDefinition(Name),
556
557    #[error("Signed decimal bytes length {0} not equal to fixed schema size {1}.")]
558    EncodeDecimalAsFixedError(usize, usize),
559
560    #[error("There is no entry for '{0}' in the lookup table: {1}.")]
561    NoEntryInLookupTable(String, String),
562
563    #[error("Can only encode value type {value_kind:?} as one of {supported_schema:?}")]
564    EncodeValueAsSchemaError {
565        value_kind: ValueKind,
566        supported_schema: Vec<SchemaKind>,
567    },
568    #[error("Internal buffer not drained properly. Re-initialize the single object writer struct!")]
569    IllegalSingleObjectWriterState,
570
571    #[error("Codec '{0}' is not supported/enabled")]
572    CodecNotSupported(String),
573
574    #[error("Invalid Avro data! Cannot read codec type from value that is not Value::Bytes.")]
575    BadCodecMetadata,
576
577    #[error("Cannot convert a slice to Uuid: {0}")]
578    UuidFromSlice(#[source] uuid::Error),
579}
580
581#[derive(thiserror::Error, PartialEq)]
582pub enum CompatibilityError {
583    #[error(
584        "Incompatible schema types! Writer schema is '{writer_schema_type}', but reader schema is '{reader_schema_type}'"
585    )]
586    WrongType {
587        writer_schema_type: String,
588        reader_schema_type: String,
589    },
590
591    #[error("Incompatible schema types! The {schema_type} should have been {expected_type:?}")]
592    TypeExpected {
593        schema_type: String,
594        expected_type: Vec<SchemaKind>,
595    },
596
597    #[error(
598        "Incompatible schemata! Field '{0}' in reader schema does not match the type in the writer schema"
599    )]
600    FieldTypeMismatch(String, #[source] Box<CompatibilityError>),
601
602    #[error("Incompatible schemata! Field '{0}' in reader schema must have a default value")]
603    MissingDefaultValue(String),
604
605    #[error("Incompatible schemata! Reader's symbols must contain all writer's symbols")]
606    MissingSymbols,
607
608    #[error("Incompatible schemata! All elements in union must match for both schemas")]
609    MissingUnionElements,
610
611    #[error("Incompatible schemata! Name and size don't match for fixed")]
612    FixedMismatch,
613
614    #[error(
615        "Incompatible schemata! The name must be the same for both schemas. Writer's name {writer_name} and reader's name {reader_name}"
616    )]
617    NameMismatch {
618        writer_name: String,
619        reader_name: String,
620    },
621
622    #[error(
623        "Incompatible schemata! Unknown type for '{0}'. Make sure that the type is a valid one"
624    )]
625    Inconclusive(String),
626}
627
628impl serde::ser::Error for Details {
629    fn custom<T: fmt::Display>(msg: T) -> Self {
630        Details::SerializeValue(msg.to_string())
631    }
632}
633
634impl serde::de::Error for Details {
635    fn custom<T: fmt::Display>(msg: T) -> Self {
636        Details::DeserializeValue(msg.to_string())
637    }
638}
639
640impl fmt::Debug for Details {
641    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
642        let mut msg = self.to_string();
643        if let Some(e) = self.source() {
644            msg.extend([": ", &e.to_string()]);
645        }
646        write!(f, "{msg}")
647    }
648}
649
650impl fmt::Debug for CompatibilityError {
651    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652        let mut msg = self.to_string();
653        if let Some(e) = self.source() {
654            msg.extend([": ", &e.to_string()]);
655        }
656        write!(f, "{msg}")
657    }
658}