Skip to main content

prost/
error.rs

1//! Protobuf encoding and decoding errors.
2
3use crate::encoding::WireType;
4use alloc::borrow::Cow;
5#[cfg(not(feature = "std"))]
6use alloc::boxed::Box;
7#[cfg(not(feature = "std"))]
8use alloc::string::String;
9#[cfg(not(feature = "std"))]
10use alloc::vec::Vec;
11use core::fmt;
12
13/// A Protobuf message decoding error.
14///
15/// `DecodeError` indicates that the input buffer does not contain a valid
16/// Protobuf message. The error details should be considered 'best effort': in
17/// general it is not possible to exactly pinpoint why data is malformed.
18#[derive(Clone, PartialEq, Eq)]
19pub struct DecodeError {
20    inner: Box<Inner>,
21}
22
23#[derive(Clone, PartialEq, Eq)]
24struct Inner {
25    /// A 'best effort' root cause description.
26    description: DecodeErrorKind,
27    /// A stack of (message, field) name pairs, which identify the specific
28    /// message type and field where decoding failed. The stack contains an
29    /// entry per level of nesting.
30    stack: Vec<(&'static str, &'static str)>,
31}
32
33impl DecodeError {
34    /// Creates a new `DecodeError` with a 'best effort' root cause description.
35    ///
36    /// Meant to be used only by `Message` implementations.
37    #[deprecated(
38        since = "0.14.2",
39        note = "This function was meant for internal use only. Because of `doc(hidden)` it was publicly available and it is actually used by users. The prost project intents to remove this function in the next breaking release."
40    )]
41    #[cold]
42    #[doc(hidden)]
43    pub fn new(description: impl Into<Cow<'static, str>>) -> DecodeError {
44        DecodeErrorKind::Other {
45            description: description.into(),
46        }
47        .into()
48    }
49
50    /// Creates a new `DecodeError` with a DecodeErrorKind::UnexpectedTypeUrl.
51    ///
52    /// Must only be used by `prost_types::Any` implementation.
53    #[doc(hidden)]
54    #[cold]
55    pub fn new_unexpected_type_url(
56        actual: impl Into<String>,
57        expected: impl Into<String>,
58    ) -> DecodeError {
59        DecodeErrorKind::UnexpectedTypeUrl {
60            actual: actual.into(),
61            expected: expected.into(),
62        }
63        .into()
64    }
65
66    /// Pushes a (message, field) name location pair on to the location stack.
67    ///
68    /// Meant to be used only by `Message` implementations.
69    #[doc(hidden)]
70    pub fn push(&mut self, message: &'static str, field: &'static str) {
71        self.inner.stack.push((message, field));
72    }
73}
74
75impl fmt::Debug for DecodeError {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        f.debug_struct("DecodeError")
78            .field("description", &self.inner.description)
79            .field("stack", &self.inner.stack)
80            .finish()
81    }
82}
83
84impl fmt::Display for DecodeError {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        f.write_str("failed to decode Protobuf message: ")?;
87        for &(message, field) in &self.inner.stack {
88            write!(f, "{message}.{field}: ")?;
89        }
90        write!(f, "{}", self.inner.description)
91    }
92}
93
94impl From<DecodeErrorKind> for DecodeError {
95    fn from(description: DecodeErrorKind) -> Self {
96        DecodeError {
97            inner: Box::new(Inner {
98                description,
99                stack: Vec::new(),
100            }),
101        }
102    }
103}
104
105#[derive(Clone, Debug, PartialEq, Eq)]
106pub(crate) enum DecodeErrorKind {
107    /// Length delimiter exceeds maximum usize value
108    LengthDelimiterTooLarge,
109    /// Invalid varint
110    InvalidVarint,
111    #[cfg(not(feature = "no-recursion-limit"))]
112    /// Recursion limit reached
113    RecursionLimitReached,
114    /// Invalid wire type value
115    InvalidWireType { value: u64 },
116    /// Invalid key value
117    InvalidKey { key: u64 },
118    /// Invalid tag value: 0
119    InvalidTag,
120    /// Invalid wire type
121    UnexpectedWireType {
122        actual: WireType,
123        expected: WireType,
124    },
125    /// Buffer underflow
126    BufferUnderflow,
127    /// Delimited length exceeded
128    DelimitedLengthExceeded,
129    /// Unexpected end group tag
130    UnexpectedEndGroupTag,
131    /// Invalid string value: data is not UTF-8 encoded
132    InvalidString,
133    /// Unexpected type URL
134    UnexpectedTypeUrl { actual: String, expected: String },
135    /// A textual description of a problem
136    Other { description: Cow<'static, str> },
137}
138
139impl fmt::Display for DecodeErrorKind {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        match self {
142            Self::LengthDelimiterTooLarge => {
143                write!(f, "length delimiter exceeds maximum usize value")
144            }
145            Self::InvalidVarint => write!(f, "invalid varint"),
146            #[cfg(not(feature = "no-recursion-limit"))]
147            Self::RecursionLimitReached => write!(f, "recursion limit reached"),
148            Self::InvalidWireType { value } => write!(f, "invalid wire type value: {value}"),
149            Self::InvalidKey { key } => write!(f, "invalid key value: {key}"),
150            Self::InvalidTag => write!(f, "invalid tag value: 0"),
151            Self::UnexpectedWireType { actual, expected } => {
152                write!(f, "invalid wire type: {actual:?} (expected {expected:?})")
153            }
154            Self::BufferUnderflow => write!(f, "buffer underflow"),
155            Self::DelimitedLengthExceeded => write!(f, "delimited length exceeded"),
156            Self::UnexpectedEndGroupTag => write!(f, "unexpected end group tag"),
157            Self::InvalidString => {
158                write!(f, "invalid string value: data is not UTF-8 encoded")
159            }
160            Self::UnexpectedTypeUrl { actual, expected } => {
161                write!(f, "unexpected type URL.type_url: expected type URL: \"{expected}\" (got: \"{actual}\")")
162            }
163            Self::Other { description } => {
164                write!(f, "{description}")
165            }
166        }
167    }
168}
169
170impl core::error::Error for DecodeError {}
171
172#[cfg(feature = "std")]
173impl From<DecodeError> for std::io::Error {
174    fn from(error: DecodeError) -> std::io::Error {
175        std::io::Error::new(std::io::ErrorKind::InvalidData, error)
176    }
177}
178
179/// A Protobuf message encoding error.
180///
181/// `EncodeError` always indicates that a message failed to encode because the
182/// provided buffer had insufficient capacity. Message encoding is otherwise
183/// infallible.
184#[derive(Copy, Clone, Debug, PartialEq, Eq)]
185pub struct EncodeError {
186    required: usize,
187    remaining: usize,
188}
189
190impl EncodeError {
191    /// Creates a new `EncodeError`.
192    pub(crate) fn new(required: usize, remaining: usize) -> EncodeError {
193        EncodeError {
194            required,
195            remaining,
196        }
197    }
198
199    /// Returns the required buffer capacity to encode the message.
200    pub fn required_capacity(&self) -> usize {
201        self.required
202    }
203
204    /// Returns the remaining length in the provided buffer at the time of encoding.
205    pub fn remaining(&self) -> usize {
206        self.remaining
207    }
208}
209
210impl fmt::Display for EncodeError {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        write!(
213            f,
214            "failed to encode Protobuf message; insufficient buffer capacity (required: {}, remaining: {})",
215            self.required, self.remaining
216        )
217    }
218}
219
220impl core::error::Error for EncodeError {}
221
222#[cfg(feature = "std")]
223impl From<EncodeError> for std::io::Error {
224    fn from(error: EncodeError) -> std::io::Error {
225        std::io::Error::new(std::io::ErrorKind::InvalidInput, error)
226    }
227}
228
229/// An error indicating that an unknown enumeration value was encountered.
230///
231/// The Protobuf spec mandates that enumeration value sets are ‘open’, so this
232/// error's value represents an integer value unrecognized by the
233/// presently used enum definition.
234#[derive(Copy, Clone, Debug, PartialEq, Eq)]
235pub struct UnknownEnumValue(pub i32);
236
237impl fmt::Display for UnknownEnumValue {
238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239        write!(f, "unknown enumeration value {}", self.0)
240    }
241}
242
243impl core::error::Error for UnknownEnumValue {}
244
245#[cfg(test)]
246mod test {
247    use super::*;
248
249    #[test]
250    fn test_push() {
251        let mut decode_error = DecodeError::from(DecodeErrorKind::InvalidVarint);
252        decode_error.push("Foo bad", "bar.foo");
253        decode_error.push("Baz bad", "bar.baz");
254
255        assert_eq!(
256            decode_error.to_string(),
257            "failed to decode Protobuf message: Foo bad.bar.foo: Baz bad.bar.baz: invalid varint"
258        );
259    }
260
261    #[cfg(feature = "std")]
262    #[test]
263    fn test_into_std_io_error() {
264        let decode_error = DecodeError::from(DecodeErrorKind::InvalidVarint);
265        let std_io_error = std::io::Error::from(decode_error);
266
267        assert_eq!(std_io_error.kind(), std::io::ErrorKind::InvalidData);
268        assert_eq!(
269            std_io_error.to_string(),
270            "failed to decode Protobuf message: invalid varint"
271        );
272    }
273}