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
- Ensure the previous instance fully exited (kill orphaned processes) and retry
- Allow only one running instance per identifier
- Simplify/normalize the identifier used in prepare() (ASCII, short, no special chars) to get a valid socket name
- 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
- Guarantee single-instance operation per identifier
- Use simple ASCII identifiers so derived socket names are valid
- Handle create_tokio errors gracefully instead of panicking; log and continue without deep-link IPC
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
- Can't create listener
- register() called before prepare()
- listen() called before prepare()
- failed to create tokio runtime
- Failed to deserialize SD
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/7fcec0198a571097.
Report an issue: GitHub.