tauri-apps/tauri · error

invalid IPC request URL

Error message

invalid IPC request URL

What it means

Tauri's IPC protocol handler parses the raw request URI of every incoming IPC message into a `Url`. If the URI cannot be parsed, this panic fires, meaning the IPC endpoint received a malformed request URL that should never happen from Tauri's own webview plumbing.

Solutions

  1. Verify the request is issued through the standard Tauri IPC invoke path, not a hand-built URL.
  2. Check any proxy/custom protocol layer that rewrites request URIs and preserve a valid absolute origin URI.
  3. Upgrade tauri — newer versions convert this to a proper error response instead of panicking.
  4. If you reproduce it in tests, capture the offending URI and validate it with `Url::parse` before sending.

Example fix

// before
fetch("/__TAURI_INTERNALS__" + badSuffix, ...)
// after
const url = new URL(window.location.origin + "/__TAURI_INTERNALS__");
fetch(url, { ...invokePayload })
Defensive patterns

Strategy: validation

Validate before calling

function isValidIpcUrl(u) {
  try { return new URL(u).origin !== 'null'; } catch { return false; }
}

Prevention

When it happens

Trigger: An IPC request reaches the custom protocol handler with a URI that `Url::parse` rejects (malformed/empty scheme-relative URI), typically from a tampered or non-standard client posting directly to the IPC endpoint.

Common situations: Custom webview runtimes or tests issuing hand-crafted IPC requests; proxies or embedded HTTP stacks mangling the request URI; fuzzing/automated tools hitting the IPC handler.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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

Appendix: source

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

      );
    }
  }

  let message = invoke_message.unwrap_or_else(|| {
    #[cfg(feature = "tracing")]
    let _span = tracing::trace_span!("ipc::request::deserialize").entered();
    serde_json::from_str::<Message>(request.body()).map_err(Into::into)
  });

  match message {
    Ok(message) => {
      let options = message.options.unwrap_or_default();

      let request = InvokeRequest {
        cmd: message.cmd,
        callback: message.callback,
        error: message.error,
        url: Url::parse(&request.uri().to_string()).expect("invalid IPC request URL"),
        body: message.payload.into(),
        headers: options.headers.0,
        invoke_key: message.invoke_key,
      };

      #[cfg(feature = "tracing")]
      let request_span = tracing::trace_span!("ipc::request::handle", cmd = request.cmd);

      webview.on_message(
        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"
          )

View on GitHub (pinned to 460ec35447)