libnyanpasu/clash-nyanpasu · error

Can't create listener

Error message

Can't create listener

What it means

After building the tokio listener with ListenerOptions::name(name).security_descriptor(sd).create_tokio(), the result is unwrapped with expect("Can't create listener"). create_tokio fails if the local socket name is invalid or already in use, or if the security descriptor is rejected — typically a leftover listener from a crashed previous instance of the same identifier.

Source

Thrown at backend/tauri-plugin-deep-link/src/windows.rs:98

        let name = ID
            .get()
            .expect("listen() called before prepare()")
            .as_str()
            .to_ns_name::<GenericNamespaced>()
            .unwrap();
        tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .expect("failed to create tokio runtime")
            .block_on(async move {
                let sdsf = "D:(A;;GA;;;WD)".to_wtf_16().unwrap();
                let sd = SecurityDescriptor::deserialize(&sdsf).expect("Failed to deserialize SD");
                let listener = ListenerOptions::new()
                    .name(name)
                    .nonblocking(ListenerNonblockingMode::Both)
                    .security_descriptor(sd)
                    .create_tokio()
                    .expect("Can't create listener");

                loop {
                    match listener.accept().await {
                        Ok(conn) => {
                            let (rx, mut tx) = conn.split();
                            let mut reader = BufReader::new(rx);
                            let mut buf = String::new();
                            if let Err(e) = reader.read_line(&mut buf).await {
                                log::error!("Error reading from connection: {e}");
                                continue;
                            }
                            buf.pop();
                            let current_pid = std::process::id();
                            let response = format!("{current_pid}\n");
                            if let Err(e) = tx.write_all(response.as_bytes()).await {
                                log::error!("Error writing to connection: {e}");
                                continue;
                            }

View on GitHub (pinned to f7dbce2997)

Solutions

  1. Ensure the previous instance fully exited (kill orphaned processes) and retry
  2. Allow only one running instance per identifier
  3. Simplify/normalize the identifier used in prepare() (ASCII, short, no special chars) to get a valid socket name
  4. On bind failure, log the OS error and retry once after a short delay instead of panicking

Example fix

// before
.prepare(identifier) // identifier: "My App (Dev) 2024!!"
// after
.prepare("com.example.myapp-dev") // safe name for socket derivation
Defensive patterns

Strategy: fallback

Validate before calling

// ensure no other instance owns the local socket name before listening
assert_single_instance(); // e.g. named-mutex or existing single-instance plugin

Try / catch

match listener_opts.create_tokio() {
    Ok(l) => serve(l),
    Err(e) => { log::error!("listener create failed: {e}"); /* degrade: skip deep-link IPC, notify user */ }
}

Prevention

When it happens

Trigger: Stale named pipe / local socket still registered by a previously crashed instance; two app instances with the same identifier racing to create the listener; invalid socket name derived from the identifier (bad characters/length).

Common situations: App crash leaving the Windows local-socket name occupied; running multiple dev builds simultaneously; identifiers with characters that map poorly to namespaced socket names.

Related errors


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