tauri-apps/tauri · error

unable to serialize response error string to json

Error message

unable to serialize response error string to json

What it means

When an IPC command returns an error and the response must be delivered back to the webview via `window.eval`, Tauri formats the error string into the callback JS. `format_callback` uses serde to JSON-encode the message; if that fails this panic fires, which should be impossible for a plain string.

Solutions

  1. Return simple, well-formed string errors from commands (avoid exotic characters in error messages).
  2. Upgrade tauri; modern versions handle callback formatting without panicking.
  3. Prefer the channel-based response path (or `Result<T, E>` with `Serialize` errors) which bypasses this eval formatting.
  4. If reproducible, log the failing error string and file an issue — it indicates a serde bug or patched dependency.

Example fix

// before
Err(format!("failed: {:?}", raw_bytes)) // may contain invalid content
// after
Err(format!("failed: {}", String::from_utf8_lossy(&raw_bytes)))
Defensive patterns

Strategy: try-catch

Try / catch

// In commands, return clean serializable errors
#[tauri::command]
fn run() -> Result<(), String> {
  inner().map_err(|e| e.to_string()) // plain UTF-8 string, no exotic bytes
}

Prevention

When it happens

Trigger: An invoke handler returns `Err(e)` whose `Display` output is serialized via `format_callback::format`, and serde_json serialization of the string unexpectedly fails.

Common situations: Extremely rare in practice; seen when custom `RuntimeError`/error types contribute control characters or when running patched/old tauri versions where the callback formatting was fragile.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of tauri-apps/tauri@460ec35447 (2026-09-18). Data as JSON: /api/errors/f4183e91ebe112f0. Report an issue: GitHub.

Appendix: source

Thrown at crates/tauri/src/ipc/protocol.rs:333

        request,
        Box::new(move |webview, cmd, response, callback, error| {
          use crate::ipc::Channel;

          #[cfg(feature = "tracing")]
          let _respond_span = tracing::trace_span!(
            parent: &request_span,
            "ipc::request::respond"
          )
          .entered();

          fn responder_eval<R: Runtime>(
            webview: &crate::Webview<R>,
            js: crate::Result<String>,
            error: CallbackFn,
          ) {
            let eval_js = js.unwrap_or_else(|e| {
              crate::ipc::format_callback::format(error, &e.to_string())
                .expect("unable to serialize response error string to json")
            });
            let _ = webview.eval(eval_js);
          }

          let can_use_channel_for_response = cmd != crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND
            && !options.custom_protocol_ipc_blocked;

          #[cfg(feature = "tracing")]
          let mime_type = match &response {
            InvokeResponse::Ok(InvokeResponseBody::Json(_)) => mime::APPLICATION_JSON,
            InvokeResponse::Ok(InvokeResponseBody::Raw(_)) => mime::APPLICATION_OCTET_STREAM,
            InvokeResponse::Err(_) => mime::APPLICATION_JSON,
          };

          #[cfg(feature = "tracing")]
          let _response_span = match &response {
            InvokeResponse::Ok(InvokeResponseBody::Json(v)) => tracing::trace_span!(
              "ipc::request::response",

View on GitHub (pinned to 460ec35447)