seanmonstar/reqwest · error · js_sys::Error

{err}

Error message

{err}

What it means

On wasm32 targets, reqwest implements From<crate::error::Error> for js_sys::Error (src/error.rs:321-326) by formatting the inner error with the Display trait into js_sys::Error::new(&format!("{err}")). The literal source string is the placeholder "{err}"; the actual JS Error.message is the rendered text of whatever reqwest error (network, decode, status, builder, etc.) was raised. This is the boundary converter that lets a Rust reqwest::Error cross into JavaScript / wasm_bindgen as a throwable JS Error, not an error kind of its own.

Source

Thrown at src/error.rs:324

}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        self.inner.source.as_ref().map(|e| &**e as _)
    }
}

#[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
impl From<crate::error::Error> for wasm_bindgen::JsValue {
    fn from(err: Error) -> wasm_bindgen::JsValue {
        js_sys::Error::from(err).into()
    }
}

#[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
impl From<crate::error::Error> for js_sys::Error {
    fn from(err: Error) -> js_sys::Error {
        js_sys::Error::new(&format!("{err}"))
    }
}

#[derive(Debug)]
pub(crate) enum Kind {
    Builder,
    Request,
    Redirect,
    #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))]
    Status(StatusCode, Option<hyper::ext::ReasonPhrase>),
    #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
    Status(StatusCode),
    Body,
    Decode,
    Upgrade,
}

// constructors

View on GitHub (pinned to 9f06fd28ab)

Solutions

  1. Inspect the JS Error.message in your catch handler against reqwest's Display strings, but prefer keeping error handling in Rust and only passing a structured/serialized payload (e.g. serde_json enum) across the boundary instead of relying on the opaque Display text.
  2. Reproduce the underlying reqwest error in Rust by logging it before conversion (e.g. web_sys::console::error_1) to see the full Kind and source chain, which the JS message loses.
  3. If you control the wasm binding, wrap reqwest results in your own enum and convert with serde_wasm_bindgen so JS sees typed error variants rather than a free-form message.
  4. For CORS/status errors, fix the server's CORS headers or use .error_for_status() deliberately and map the Status kind yourself.

Example fix

// before: opaque JS message
#[wasm_bindgen]
pub async fn fetch_text(url: &str) -> Result<String, JsValue> {
    let resp = reqwest::get(url).await?.text().await?;
    Ok(resp)
}

// after: structured error crossing the boundary
#[derive(serde::Serialize)]
pub enum FetchErr { Network, Status(u16), Body }

#[wasm_bindgen]
pub async fn fetch_text(url: &str) -> Result<String, JsValue> {
    let resp = reqwest::get(url).await.map_err(|_| FetchErr::Network.to_string())?;
    let status = resp.status().as_u16();
    let text = resp.text().await.map_err(|_| FetchErr::Body.to_string())?;
    if status >= 400 { return Err(JsValue::from(FetchErr::Status(status).to_string())); }
    Ok(text)
}
Defensive patterns

Strategy: try-catch

Type guard

// In JS consumer code
function isReqwestError(e: unknown): e is Error {
  return e instanceof Error && typeof (e as Error).message === "string";
}

Try / catch

// JS side catching the wasm-thrown error
try {
  const text = await fetch_text(url);
} catch (e) {
  // e.message is reqwest's Display string; surface to user or retry on network/CORS messages
  if (/sending request|network|Failed to fetch/i.test((e as Error).message)) {
    // transient, retry with backoff
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Any failed reqwest call in a wasm32-unknown-unknown build that is converted to a JsValue or js_sys::Error and propagated to JS: a fetch() that returned a non-2xx with .error_for_status(), a CORS rejection, an abort via AbortController, a URL parse failure, or a network failure in the browser. The conversion runs whenever ?-propagation reaches a function returning Result<_, wasm_bindgen::JsValue> or Result<_, js_sys::Error>.

Common situations: Browser-side reqwest builds (wasm) where the JS consumer catches the error and inspects .message. Confusion arises because the message is the reqwest Display string (e.g. "error sending request", "HTTP status client error (404 Not Found) for url (...)") rather than a stable error code. Version upgrades of reqwest that change Display wording silently change the JS-visible message. CORS misconfigurations are the most common real-world trigger.


AI-assisted analysis of seanmonstar/reqwest@9f06fd28ab (2026-08-10). Data as JSON: /api/errors/c02934bd4ad98f0f. Report an issue: GitHub.