forest/rpc/
error.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use std::fmt::{self, Display};
5
6use crate::rpc::eth::errors::EthErrors;
7use jsonrpsee::{
8    core::ClientError,
9    types::error::{self, ErrorCode, ErrorObjectOwned},
10};
11
12/// Trait for errors that can provide additional RPC error data
13pub trait RpcErrorData {
14    /// Return the error code to use in RPC responses
15    fn error_code(&self) -> Option<i32> {
16        None
17    }
18
19    /// Return the error message to use in RPC responses
20    fn error_message(&self) -> Option<String> {
21        None
22    }
23
24    /// Return additional data to include in the RPC error response
25    fn error_data(&self) -> Option<serde_json::Value> {
26        None
27    }
28}
29
30/// An error returned _by the remote server_, not due to e.g serialization errors,
31/// protocol errors, or the connection failing.
32#[derive(derive_more::From, derive_more::Into, Debug, PartialEq)]
33pub struct ServerError {
34    inner: ErrorObjectOwned,
35}
36
37/// According to the [JSON-RPC 2.0 spec](https://www.jsonrpc.org/specification#response_object),
38/// the error codes from -32000 to -32099 are reserved for implementation-defined server-errors.
39/// We define them here.
40pub(crate) mod implementation_defined_errors {
41    /// This error indicates that the method is not supported by the current version of the Forest
42    /// node. Note that it's not the same as not found, as we are explicitly not supporting it,
43    /// e.g., because it's deprecated or Lotus is doing the same.
44    pub(crate) const UNSUPPORTED_METHOD: i32 = -32001;
45}
46
47impl ServerError {
48    pub fn new(
49        code: i32,
50        message: impl Display,
51        data: impl Into<Option<serde_json::Value>>,
52    ) -> Self {
53        Self {
54            inner: ErrorObjectOwned::owned(code, message.to_string(), data.into()),
55        }
56    }
57    pub fn message(&self) -> &str {
58        self.inner.message()
59    }
60    pub fn known_code(&self) -> ErrorCode {
61        self.inner.code().into()
62    }
63    /// We are only including this method to get the JSON Schemas for our OpenRPC
64    /// machinery
65    pub fn stubbed_for_openrpc() -> Self {
66        Self::new(
67            4528,
68            "unimplemented",
69            Some(
70                "This method is stubbed as part of https://github.com/ChainSafe/forest/issues/4528"
71                    .into(),
72            ),
73        )
74    }
75
76    pub fn unsupported_method() -> Self {
77        Self::new(
78            implementation_defined_errors::UNSUPPORTED_METHOD,
79            "unsupported method",
80            Some("This method is not supported by the current version of the Forest node".into()),
81        )
82    }
83
84    pub fn inner(&self) -> &ErrorObjectOwned {
85        &self.inner
86    }
87}
88
89impl<E: std::error::Error + RpcErrorData + 'static> From<E> for ServerError {
90    fn from(error: E) -> Self {
91        let code = error.error_code().unwrap_or(error::INTERNAL_ERROR_CODE);
92        let message = error.error_message().unwrap_or_else(|| error.to_string());
93        let data = error.error_data();
94
95        Self::new(code, message, data)
96    }
97}
98
99// Default implementation for anyhow::Error to handle downcasting once
100impl From<anyhow::Error> for ServerError {
101    fn from(error: anyhow::Error) -> Self {
102        // Try to downcast to known RpcErrorData implementations
103        if let Some(eth_error) = error.downcast_ref::<EthErrors>() {
104            return eth_error.clone().into();
105        }
106
107        // Default fallback
108        Self::internal_error(error.to_string(), None)
109    }
110}
111
112impl Display for ServerError {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        f.write_str("JSON-RPC error:\n")?;
115        f.write_fmt(format_args!("\tcode: {}\n", self.inner.code()))?;
116        f.write_fmt(format_args!("\tmessage: {}\n", self.inner.message()))?;
117        if let Some(data) = self.inner.data() {
118            f.write_fmt(format_args!("\tdata: {data}\n"))?
119        }
120        Ok(())
121    }
122}
123
124impl std::error::Error for ServerError {}
125
126macro_rules! ctor {
127    ($($ctor:ident { $code:expr })*) => {
128        $(
129            impl ServerError {
130                pub fn $ctor(message: impl Display, data: impl Into<Option<serde_json::Value>>) -> Self {
131                    Self::new($code, message, data)
132                }
133            }
134        )*
135    }
136}
137
138ctor! {
139    parse_error { error::PARSE_ERROR_CODE }
140    internal_error { error::INTERNAL_ERROR_CODE }
141    invalid_params { error::INVALID_PARAMS_CODE }
142    method_not_found { error::METHOD_NOT_FOUND_CODE }
143}
144
145macro_rules! from2internal {
146    ($($ty:ty),* $(,)?) => {
147        $(
148            impl From<$ty> for ServerError {
149                fn from(it: $ty) -> Self {
150                    Self::internal_error(it, None)
151                }
152            }
153        )*
154    };
155}
156
157from2internal! {
158    String,
159    base64::DecodeError,
160    cid::multibase::Error,
161    crate::chain::store::Error,
162    crate::chain_sync::TipsetValidationError,
163    crate::key_management::Error,
164    crate::libp2p::ParseError,
165    crate::message_pool::Error,
166    crate::state_manager::Error,
167    fil_actors_shared::fvm_ipld_amt::Error,
168    futures::channel::oneshot::Canceled,
169    fvm_ipld_encoding::Error,
170    fvm_shared4::address::Error,
171    jsonwebtoken::errors::Error,
172    std::io::Error,
173    std::time::SystemTimeError,
174    tokio::task::JoinError,
175    fil_actors_shared::fvm_ipld_hamt::Error,
176    flume::RecvError,
177    fil_actors_shared::v12::ActorError,
178    fil_actors_shared::v13::ActorError,
179    fil_actors_shared::v14::ActorError,
180    fil_actors_shared::v15::ActorError,
181    fil_actors_shared::v16::ActorError,
182    serde_json::Error,
183    jsonrpsee::core::client::error::Error,
184}
185
186impl From<ServerError> for ClientError {
187    fn from(value: ServerError) -> Self {
188        Self::Call(value.inner)
189    }
190}
191
192impl<T> From<flume::SendError<T>> for ServerError {
193    fn from(e: flume::SendError<T>) -> Self {
194        Self::internal_error(e, None)
195    }
196}
197
198impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ServerError {
199    fn from(e: tokio::sync::mpsc::error::SendError<T>) -> Self {
200        Self::internal_error(e, None)
201    }
202}