libnyanpasu/clash-nyanpasu · error
listen() called before prepare()
Error message
listen() called before prepare()
What it means
listen() on macOS reads the identifier from the ID OnceCell to build the debug Unix socket path. If prepare() was not called first, ID.get() is None and this expect() panics. It is a lifecycle-order guard: prepare() must always precede listen().
Source
Thrown at backend/tauri-plugin-deep-link/src/macos.rs:105
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()")
);
#[cfg(debug_assertions)]
if HANDLER
.set(match UnixStream::connect(&addr) {
Ok(_) => Mutex::new(Box::new(secondary_handler)),
Err(err) => {
log::error!("Error creating socket listener: {}", err.to_string());
if err.kind() == ErrorKind::ConnectionRefused {
let _ = remove_file(&addr);
}
Mutex::new(Box::new(handler))
}
})
.is_err()
{
return Err(std::io::Error::new(
ErrorKind::AlreadyExists,View on GitHub (pinned to f7dbce2997)
Solutions
- Always call prepare(identifier) before listen()
- Add prepare() at the top of your deep-link registration helper so ordering cannot regress
- In tests, call prepare() in a setup fixture before touching listen()
Example fix
// before
listen(|url| println!("{}", url))?;
// after
prepare("com.example.app");
listen(|url| println!("{}", url))?; Defensive patterns
Strategy: validation
Validate before calling
assert!(ID.get().is_some(), "call prepare() before listen()");
Type guard
fn is_prepared() -> bool { ID.get().is_some() } Prevention
- Enforce prepare-then-listen ordering in one setup function
- Do not call listen() from hooks that run before plugin prepare
- Add an init-order integration test
When it happens
Trigger: Calling listen(handler) before prepare(identifier); conditional startup logic that registers deep links but skips prepare(); re-entrancy where listen is invoked from a hook that runs before plugin prepare.
Common situations: Copy-pasted plugin setup missing the prepare() call; app refactors that moved listen() earlier than prepare(); tests exercising listen() in isolation.
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
- URL event received before prepare() was called
- prepare() called more than once with different identifiers.
- prepare() called more than once with different identifiers.
- Can't create listener
- register() called before prepare()
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/b3029b107224ecc3.
Report an issue: GitHub.