libnyanpasu/clash-nyanpasu · error

listen() called before prepare()

Error message

listen() called before prepare()

What it means

The plugin's listen() spawns a thread that builds a Unix socket path from the OnceCell ID set by prepare(). If listen() is called before prepare(), ID.get() is None and the .expect panics. Like register(), it enforces that prepare() initializes the plugin before listening for deep-link events.

Source

Thrown at backend/tauri-plugin-deep-link/src/linux.rs:107

            .file_name()
            .ok_or_else(|| Error::new(
                ErrorKind::NotFound,
                "Couldn't get file name of current executable.",
            ))?
            .to_string_lossy()
    ));

    remove_file(&target)?;
    target.pop();

    Ok(())
}

pub fn listen<F: FnMut(String) + Send + 'static>(mut handler: F) -> Result<()> {
    std::thread::spawn(move || {
        let addr = format!(
            "/tmp/{}-deep-link.sock",
            ID.get().expect("listen() called before prepare()")
        );

        let listener = UnixListener::bind(addr).expect("Can't create listener");

        for stream in listener.incoming() {
            match stream {
                Ok(mut stream) => {
                    let mut buffer = String::new();
                    if let Err(io_err) = stream.read_to_string(&mut buffer) {
                        log::error!("Error reading incoming connection: {}", io_err.to_string());
                    };

                    handler(dbg!(buffer));
                }
                Err(err) => {
                    log::error!("Incoming connection failed: {}", err);
                    continue;
                }

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Ensure prepare() runs before listen(); wire the plugin through the standard Tauri builder.
  2. If listen() is reached via register(), fix the ordering at the register() call site (see error 247).
  3. Defer listen() until after plugin initialization completes (e.g. in the app's setup callback).
  4. Add an early assertion/log in your bootstrap that prepare() has run before spawning listeners.

Example fix

// before
listen(|url| handle(url));
deep_link::prepare("com.example.app");

// after
deep_link::prepare("com.example.app");
listen(|url| handle(url));
Defensive patterns

Strategy: validation

Validate before calling

// Ensure initialization order
if !deep_link::is_prepared() { deep_link::prepare(APP_ID); }
deep_link::listen(handler)?;

Try / catch

catch_unwind around listen(); on panic log "prepare() must run before listen()" and re-init.

Prevention

When it happens

Trigger: Calling deep_link listen() (directly or indirectly through register(), which calls it) before prepare() populated ID.

Common situations: Startup-order mistakes in manual plugin wiring; tests that invoke listen() standalone; custom launchers that start listening before the plugin's prepare hook executes.

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/7170fc20457b5a5f. Report an issue: GitHub.