libnyanpasu/clash-nyanpasu · error

register() called before prepare()

Error message

register() called before prepare()

What it means

register() on Windows writes a registry key whose display value is 'URL:<identifier>', reading the identifier from the ID OnceCell. If prepare() was not called first, ID.get() is None and this expect() panics. prepare() must precede register().

Solutions

  1. Call prepare(identifier) before register()
  2. Move both calls into one setup function so ordering is enforced
  3. In tests, prepare() in a common setup step before register()

Example fix

// before
register("myapp");
// after
prepare("com.example.myapp");
register("myapp");
Defensive patterns

Strategy: validation

Validate before calling

assert!(ID.get().is_some(), "call prepare() before register()");

Type guard

fn is_prepared() -> bool { ID.get().is_some() }

Prevention

When it happens

Trigger: Calling register() before prepare(); registering the URL scheme during installer/init code that runs before the plugin's prepare(); conditional setup that skips prepare().

Common situations: Custom scheme registration invoked early in app bootstrap; refactors that moved register() before prepare(); tests calling register() standalone.

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

Appendix: source

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

pub fn register<F: FnMut(String) + Send + 'static>(schemes: &[&str], handler: F) -> Result<()> {
    listen(handler)?;

    for scheme in schemes {
        let hkcu = RegKey::predef(HKEY_CURRENT_USER);
        let base = Path::new("Software").join("Classes").join(scheme);

        let exe = tauri_utils::platform::current_exe()?
            .display()
            .to_string()
            .replace("\\\\?\\", "");

        let (key, _) = hkcu.create_subkey(&base)?;
        key.set_value(
            "",
            &format!(
                "URL:{}",
                ID.get().expect("register() called before prepare()")
            ),
        )?;
        key.set_value("URL Protocol", &"")?;

        let (icon, _) = hkcu.create_subkey(base.join("DefaultIcon"))?;
        icon.set_value("", &format!("\"{}\",0", exe))?;

        let (cmd, _) = hkcu.create_subkey(base.join("shell").join("open").join("command"))?;

        cmd.set_value("", &format!("\"{}\" \"%1\"", exe))?;
    }

    Ok(())
}

pub fn unregister(schemes: &[&str]) -> Result<()> {
    for scheme in schemes {
        let hkcu = RegKey::predef(HKEY_CURRENT_USER);

View on GitHub (pinned to f7dbce2997)