Skip to main content

forest/rpc/
error.rs

1// Copyright 2019-2026 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, derive_more::Deref, 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
58    pub fn known_code(&self) -> ErrorCode {
59        self.inner.code().into()
60    }
61
62    /// We are only including this method to get the JSON schemas for our OpenRPC
63    /// machinery
64    pub fn stubbed_for_openrpc() -> Self {
65        Self::new(
66            4528,
67            "unimplemented",
68            Some(
69                "This method is stubbed as part of https://github.com/ChainSafe/forest/issues/4528"
70                    .into(),
71            ),
72        )
73    }
74
75    pub fn unsupported_method() -> Self {
76        Self::new(
77            implementation_defined_errors::UNSUPPORTED_METHOD,
78            "unsupported method",
79            Some("This method is not supported by the current version of the Forest node".into()),
80        )
81    }
82}
83
84impl<E: std::error::Error + RpcErrorData + 'static> From<E> for ServerError {
85    fn from(error: E) -> Self {
86        let code = error.error_code().unwrap_or(error::INTERNAL_ERROR_CODE);
87        let message = error.error_message().unwrap_or_else(|| error.to_string());
88        let data = error.error_data();
89
90        Self::new(code, message, data)
91    }
92}
93
94// Default implementation for anyhow::Error to handle downcasting once
95impl From<anyhow::Error> for ServerError {
96    fn from(error: anyhow::Error) -> Self {
97        // Try to downcast to known RpcErrorData implementations
98        if let Some(eth_error) = error.downcast_ref::<EthErrors>() {
99            return eth_error.clone().into();
100        }
101
102        // Default fallback, not using `format!("{e:#}")` here to match Lotus error
103        Self::internal_error(error.to_string(), None)
104    }
105}
106
107impl Display for ServerError {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        f.write_str("JSON-RPC error:\n")?;
110        f.write_fmt(format_args!("\tcode: {}\n", self.inner.code()))?;
111        f.write_fmt(format_args!("\tmessage: {}\n", self.inner.message()))?;
112        if let Some(data) = self.inner.data() {
113            f.write_fmt(format_args!("\tdata: {data}\n"))?
114        }
115        Ok(())
116    }
117}
118
119impl std::error::Error for ServerError {}
120
121macro_rules! ctor {
122    ($($ctor:ident { $code:expr })*) => {
123        $(
124            impl ServerError {
125                pub fn $ctor(message: impl Display, data: impl Into<Option<serde_json::Value>>) -> Self {
126                    Self::new($code, message, data)
127                }
128            }
129        )*
130    }
131}
132
133ctor! {
134    parse_error { error::PARSE_ERROR_CODE }
135    internal_error { error::INTERNAL_ERROR_CODE }
136    invalid_params { error::INVALID_PARAMS_CODE }
137    method_not_found { error::METHOD_NOT_FOUND_CODE }
138}
139
140macro_rules! from2internal {
141    ($($ty:ty),* $(,)?) => {
142        $(
143            impl From<$ty> for ServerError {
144                fn from(it: $ty) -> Self {
145                    Self::internal_error(it, None)
146                }
147            }
148        )*
149    };
150}
151
152from2internal! {
153    String,
154    base64::DecodeError,
155    cid::multibase::Error,
156    crate::chain::store::Error,
157    crate::chain_sync::TipsetValidationError,
158    crate::key_management::Error,
159    crate::libp2p::ParseError,
160    crate::message_pool::Error,
161    crate::state_manager::Error,
162    fil_actors_shared::fvm_ipld_amt::Error,
163    futures::channel::oneshot::Canceled,
164    fvm_ipld_encoding::Error,
165    fvm_shared4::address::Error,
166    jsonwebtoken::errors::Error,
167    std::io::Error,
168    std::time::SystemTimeError,
169    tokio::task::JoinError,
170    fil_actors_shared::fvm_ipld_hamt::Error,
171    flume::RecvError,
172    fil_actors_shared::v12::ActorError,
173    fil_actors_shared::v13::ActorError,
174    fil_actors_shared::v14::ActorError,
175    fil_actors_shared::v15::ActorError,
176    fil_actors_shared::v16::ActorError,
177    serde_json::Error,
178    jsonrpsee::core::client::error::Error,
179}
180
181impl From<ServerError> for ClientError {
182    fn from(value: ServerError) -> Self {
183        Self::Call(value.inner)
184    }
185}
186
187impl<T> From<flume::SendError<T>> for ServerError {
188    fn from(e: flume::SendError<T>) -> Self {
189        Self::internal_error(e, None)
190    }
191}
192
193impl<T> From<tokio::sync::mpsc::error::SendError<T>> for ServerError {
194    fn from(e: tokio::sync::mpsc::error::SendError<T>) -> Self {
195        Self::internal_error(e, None)
196    }
197}