libnyanpasu/clash-nyanpasu · error
prepare() called more than once with different identifiers.
Error message
prepare() called more than once with different identifiers.
What it means
The deep-link plugin stores the app identifier once via OnceCell (ID.set). If prepare() is invoked a second time with a different identifier string, set() returns Err and the .expect panics. This guards the invariant that one process has exactly one deep-link identifier for the Windows foreground-window workaround.
Source
Thrown at backend/tauri-plugin-deep-link/src/windows.rs:202
let pid = buf.parse::<u32>().unwrap_or(ASFW_ANY);
unsafe {
let success = AllowSetForegroundWindow(pid) != 0;
if !success {
eprintln!("AllowSetForegroundWindow failed.");
}
}
std::process::exit(0);
}
Err(e) => {
eprintln!("Failed to connect to local socket: {e}");
std::thread::sleep(std::time::Duration::from_millis(1));
}
};
}
});
ID.set(identifier.to_string())
.expect("prepare() called more than once with different identifiers.");
}
/// Send a dummy keypress event so AllowSetForegroundWindow can succeed
fn dummy_keypress() {
let keyboard_input_down = KEYBDINPUT {
wVk: 0, // This doesn't correspond to any actual keyboard key, but should still function for the workaround.
dwExtraInfo: 0,
wScan: 0,
time: 0,
dwFlags: 0,
};
let mut keyboard_input_up = keyboard_input_down;
keyboard_input_up.dwFlags = 0x0002; // KEYUP flag
let input_down_u = INPUT_0 {
ki: keyboard_input_down,
};View on GitHub (pinned to f7dbce2997)
Solutions
- Call prepare() only once per process with a constant identifier string
- Verify the plugin isn't registered twice in the Tauri builder chain
- Make the identifier a compile-time constant (e.g. from tauri.conf.json identifier) rather than a computed value
- In tests, run each identifier in a separate process or reset state between runs
Example fix
// before
prepare(config.identifier.clone() + "-v2");
// after
prepare(env!("CARGO_PKG_NAME").to_string()); // stable, same value every call Defensive patterns
Strategy: validation
Validate before calling
static INITIALIZED: AtomicBool = AtomicBool::new(false); assert!(!INITIALIZED.swap(true, Ordering::SeqCst), "prepare() already called");
Try / catch
if ID.set(identifier.to_string()).is_err() {
eprintln!("prepare() called twice with different identifiers");
} Prevention
- Register the deep-link plugin exactly once in the Tauri builder
- Derive the identifier from a single constant source (tauri.conf.json)
When it happens
Trigger: Calling plugin prepare()/deep_link APIs twice during startup with differing identifier values — typically registering the deep-link plugin twice, or re-invoking prepare after config reload changed the identifier.
Common situations: Accidentally adding the deep-link plugin (or a plugin depending on it) twice to the Tauri builder; dynamic identifier derived from config that differs across initialization passes; test harnesses rebuilding the app builder per test with different identifiers in one process.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Local socket too many crashes
- Failed to get module handle: {err}
- prepare() called more than once with different identifiers.
- URL event received before prepare() was called
- listen() called before prepare()
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/9a30583a47524b77.
Report an issue: GitHub.