libnyanpasu/clash-nyanpasu · error

URL event received before prepare() was called

Error message

URL event received before prepare() was called

What it means

In debug builds on macOS, the secondary-instance handler reads the identifier from the ID OnceCell to build the /tmp/<id>-deep-link.sock path. If prepare() was never called, ID.get() returns None and this expect() panics. It enforces that prepare() precedes any URL event handling.

Source

Thrown at backend/tauri-plugin-deep-link/src/macos.rs:88

            let mut cb = HANDLER.get().unwrap().lock().unwrap();
            cb(s);
        }
    }
);

impl Handler {
    pub fn new() -> Retained<Self> {
        let cls = Self::class();
        unsafe { msg_send_id![msg_send_id![cls, alloc], init] }
    }
}

#[cfg(debug_assertions)]
fn secondary_handler(s: String) {
    let addr = format!(
        "/tmp/{}-deep-link.sock",
        ID.get()
            .expect("URL event received before prepare() was called")
    );
    if let Ok(mut stream) = UnixStream::connect(addr) {
        if let Err(io_err) = stream.write_all(s.as_bytes()) {
            log::error!(
                "Error sending message to primary instance: {}",
                io_err.to_string()
            );
        };
    }
    std::process::exit(0);
}

pub fn listen<F: FnMut(String) + Send + 'static>(handler: F) -> Result<()> {
    #[cfg(debug_assertions)]
    let addr = format!(
        "/tmp/{}-deep-link.sock",
        ID.get().expect("listen() called before prepare()")
    );

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Call prepare(identifier) before register()/listen() in the app setup
  2. Ensure every code path that can handle a URL runs prepare() first
  3. If the identifier is dynamic, prepare() it before spawning any secondary-instance handler

Example fix

// before
register("myapp");
// after
prepare("com.example.myapp");
register("myapp");
Defensive patterns

Strategy: validation

Validate before calling

assert!(ID.get().is_some(), "call prepare() before handling deep links");

Type guard

fn is_prepared() -> bool { ID.get().is_some() }

Prevention

When it happens

Trigger: A secondary instance receives a URL event (secondary_handler fires) before the app code called prepare(identifier); calling listen()/register() without prepare() first so no identifier was stored.

Common situations: Forgetting prepare() in a custom plugin setup; registering deep-link handling conditionally so prepare() is skipped on some code path while listen() still runs; macOS dev builds where single-instance handoff fires early.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/f21300167e452ac6. Report an issue: GitHub.