leptos-rs/leptos · error

At {caller}, you call `to_server_error()` or use `server_fn_

Error message

At {caller}, you call `to_server_error()` or use `server_fn_error!` with a value that does not implement `Clone` and either `Error` or `Display`.

What it means

server_fn's error conversion trait ViaError only accepts error types that are Clone plus either Error or Display; the blanket fallback impl for WrapError exists only to produce a compile-targeted panic via #[track_caller]. Hitting it means to_server_error() (or the server_fn_error! macro) was invoked at runtime with a value that fails those trait bounds in the chosen conversion path.

Source

Thrown at server_fn/src/error.rs:150

    fn to_server_error(&self) -> ServerFnError<E> {
        ServerFnError::WrappedServerError(self.0.clone())
    }
}

// If it doesn't impl Error, but does impl Display and Clone,
// we can still wrap it in String form
impl<E: Display + Clone> ViaError<E> for &WrapError<E> {
    fn to_server_error(&self) -> ServerFnError<E> {
        ServerFnError::ServerError(self.0.to_string())
    }
}

// This is what happens if someone tries to pass in something that does
// not meet the above criteria
impl<E> ViaError<E> for WrapError<E> {
    #[track_caller]
    fn to_server_error(&self) -> ServerFnError<E> {
        panic!(
            "At {}, you call `to_server_error()` or use  `server_fn_error!` \
             with a value that does not implement `Clone` and either `Error` \
             or `Display`.",
            std::panic::Location::caller()
        );
    }
}

/// A type that can be used as the return type of the server function for easy error conversion with `?` operator.
/// This type can be replaced with any other error type that implements `FromServerFnError`.
///
/// Unlike [`ServerFnErrorErr`], this does not implement [`Error`](trait@std::error::Error).
/// This means that other error types can easily be converted into it using the
/// `?` operator.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(
    feature = "rkyv",
    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Make the error type implement Clone and either std::error::Error or Display (derive Clone, thiserror::Error, and Display).
  2. Ensure the error type does not contain non-Clone fields; wrap them in Arc or convert to a String.
  3. Use the From-based conversion path expected by the server function's custom error type instead of relying on WrapError's fallback.
  4. Check the #[server] function's error type parameter matches a type implementing the required traits.

Example fix

// before
struct ApiError { inner: ReqwestError } // not Clone -> falls into panic impl

// after
#[derive(Debug, Clone, thiserror::Error)]
enum ApiError {
    #[error("request failed: {0}")]
    Request(String), // Clone + Display + Error
}
Defensive patterns

Strategy: type-guard

Validate before calling

fn valid_server_fn_error<E: Clone + std::fmt::Display>(_e: &E) {} // compile-time check before using server_fn_error!

Type guard

fn is_convertible_error<E: Clone + std::fmt::Display + std::error::Error>(_e: &E) -> bool { true } // bounds act as the guard

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| err.to_server_error()));

Prevention

When it happens

Trigger: Calling .to_server_error() on a wrapped error type, or using server_fn_error!/WrapError in a server function whose custom error type E does not implement Clone together with std::error::Error or Display, so the fallback impl is selected.

Common situations: Defining a custom ServerFnErrorErr/error type missing Clone or Display; returning non-cloneable error types (e.g. containing a Box<dyn Error> or a reqwest::Error) from server functions; type inference silently choosing the ViaError fallback impl after refactoring the error type.

Related errors


AI-assisted analysis of leptos-rs/leptos@32d20f6c9d (2026-09-01). Data as JSON: /api/errors/d2bbaa5e37887ac6. Report an issue: GitHub.