{"record":{"id":"a3710fb0ea677c0c","repo":"tauri-apps/tauri","slug":"runtime-already-initialized","errorCode":null,"errorMessage":"runtime already initialized","messagePattern":"runtime already initialized","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tauri/src/async_runtime.rs","lineNumber":261,"sourceCode":"///   tauri::async_runtime::set(tokio::runtime::Handle::current());\n///\n///   // bootstrap the tauri app...\n///   // tauri::Builder::default().run().unwrap();\n/// }\n///\n/// async fn do_something() {}\n/// ```\n///\n/// # Panics\n///\n/// Panics if the runtime is already set.\npub fn set(handle: TokioHandle) {\n  RUNTIME\n    .set(GlobalRuntime {\n      runtime: None,\n      handle: RuntimeHandle::Tokio(handle),\n    })\n    .unwrap_or_else(|_| panic!(\"runtime already initialized\"))\n}\n\n/// Returns a handle of the async runtime.\npub fn handle() -> RuntimeHandle {\n  let runtime = RUNTIME.get_or_init(default_runtime);\n  runtime.handle()\n}\n\n#[track_caller]\n/// Runs a future to completion on runtime.\npub fn block_on<F: Future>(task: F) -> F::Output {\n  let runtime = RUNTIME.get_or_init(default_runtime);\n  runtime.block_on(task)\n}\n\n#[track_caller]\n/// Spawns a future onto the runtime.\npub fn spawn<F>(task: F) -> JoinHandle<F::Output>","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/tauri-apps/tauri/blob/460ec35447493200d64290dd7f015d5a91d0fd58/crates/tauri/src/async_runtime.rs#L243-L279","documentation":"async_runtime::set installs a global tokio runtime handle into a `OnceCell`-style static. `OnceCell::set` fails if a runtime was already stored, and the library panics with 'runtime already initialized' because two runtimes cannot coexist as the global default.","triggerScenarios":"Calling `tauri::async_runtime::set(handle)` more than once, or calling it after `async_runtime::handle()`/any spawned async work lazily initialized the default runtime via `get_or_init(default_runtime)`.","commonSituations":"Embedding Tauri in an app that already built a tokio runtime and calls set() in multiple places (e.g. both main and a test helper); running tests in parallel where each test calls set(); initializing Tauri after other code already triggered the lazy default runtime.","solutions":["Call `set` exactly once, at process startup, before any Tauri async API is used.","Guard with `set` semantics: only call set if `async_runtime::RUNTIME`/runtime not yet initialized, or route all set() calls through a once-initialized helper (std::sync::Once).","If a default runtime was already lazily created, do not call set at all — use `async_runtime::handle()` to reuse it.","In tests, share one runtime across the test binary or use a serial test harness instead of calling set per-test."],"exampleFix":"// before\nfn main() {\n  let rt = tokio::runtime::Runtime::new().unwrap();\n  tauri::async_runtime::set(rt.handle().clone());\n  init_app(); // also calls set() -> panic\n}\n// after\nstatic INIT: std::sync::Once = std::sync::Once::new();\nfn init_runtime(handle: tokio::runtime::Handle) {\n  INIT.call_once(|| tauri::async_runtime::set(handle));\n}","handlingStrategy":"validation","validationCode":"use std::sync::Once;\nstatic RT_INIT: Once = Once::new();\nfn ensure_runtime(handle: tauri::async_runtime::RuntimeHandle) {\n  RT_INIT.call_once(|| tauri::async_runtime::set(handle));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call async_runtime::set exactly once, before any Tauri async API","Never call set after handle()/spawn() which lazily create the default runtime","Centralize runtime init in a Once or main() only","In tests, share a single runtime instead of per-test set()"],"tags":["rust","tauri","tokio","runtime","panic"],"backgroundTag":"invalid-state-transition","analyzedSha":"460ec35447493200d64290dd7f015d5a91d0fd58","analyzedAt":"2026-09-18T23:55:51.277Z","contentChangedAt":"2026-09-18T23:55:51.277Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}