{"record":{"id":"7170fc20457b5a5f","repo":"libnyanpasu/clash-nyanpasu","slug":"listen-called-before-prepare","errorCode":null,"errorMessage":"listen() called before prepare()","messagePattern":"listen\\(\\) called before prepare\\(\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri-plugin-deep-link/src/linux.rs","lineNumber":107,"sourceCode":"            .file_name()\n            .ok_or_else(|| Error::new(\n                ErrorKind::NotFound,\n                \"Couldn't get file name of current executable.\",\n            ))?\n            .to_string_lossy()\n    ));\n\n    remove_file(&target)?;\n    target.pop();\n\n    Ok(())\n}\n\npub fn listen<F: FnMut(String) + Send + 'static>(mut handler: F) -> Result<()> {\n    std::thread::spawn(move || {\n        let addr = format!(\n            \"/tmp/{}-deep-link.sock\",\n            ID.get().expect(\"listen() called before prepare()\")\n        );\n\n        let listener = UnixListener::bind(addr).expect(\"Can't create listener\");\n\n        for stream in listener.incoming() {\n            match stream {\n                Ok(mut stream) => {\n                    let mut buffer = String::new();\n                    if let Err(io_err) = stream.read_to_string(&mut buffer) {\n                        log::error!(\"Error reading incoming connection: {}\", io_err.to_string());\n                    };\n\n                    handler(dbg!(buffer));\n                }\n                Err(err) => {\n                    log::error!(\"Incoming connection failed: {}\", err);\n                    continue;\n                }","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri-plugin-deep-link/src/linux.rs#L89-L125","documentation":"The plugin's listen() spawns a thread that builds a Unix socket path from the OnceCell ID set by prepare(). If listen() is called before prepare(), ID.get() is None and the .expect panics. Like register(), it enforces that prepare() initializes the plugin before listening for deep-link events.","triggerScenarios":"Calling deep_link listen() (directly or indirectly through register(), which calls it) before prepare() populated ID.","commonSituations":"Startup-order mistakes in manual plugin wiring; tests that invoke listen() standalone; custom launchers that start listening before the plugin's prepare hook executes.","solutions":["Ensure prepare() runs before listen(); wire the plugin through the standard Tauri builder.","If listen() is reached via register(), fix the ordering at the register() call site (see error 247).","Defer listen() until after plugin initialization completes (e.g. in the app's setup callback).","Add an early assertion/log in your bootstrap that prepare() has run before spawning listeners."],"exampleFix":"// before\nlisten(|url| handle(url));\ndeep_link::prepare(\"com.example.app\");\n\n// after\ndeep_link::prepare(\"com.example.app\");\nlisten(|url| handle(url));","handlingStrategy":"validation","validationCode":"// Ensure initialization order\nif !deep_link::is_prepared() { deep_link::prepare(APP_ID); }\ndeep_link::listen(handler)?;","typeGuard":null,"tryCatchPattern":"catch_unwind around listen(); on panic log \"prepare() must run before listen()\" and re-init.","preventionTips":["Initialize prepare() before any listen()/register() call.","Wire the plugin via the Tauri builder rather than manual calls.","Start listeners in the setup hook, after plugin init.","Fix ordering at register() call sites (register calls listen indirectly)."],"tags":["panic","initialization-order","deep-link","linux"],"backgroundTag":"invalid-state-transition","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}