tauri-apps/tauri · error

Failed to setup app

Error message

Failed to setup app: {e}

What it means

When the app's event loop fires `Ready`, Tauri runs the user's `setup` hook. If that hook returns an error, the app panics with this message instead of continuing to run, because a failed setup leaves the app in an unusable state. The original error is embedded in `{e}`.

Solutions

  1. Read the embedded `{e}` — it names the real cause (plugin init failure, window creation error, etc.) and fix that root issue.
  2. Return `Ok(())` from your `.setup()` hook only after validating fallible operations; handle recoverable errors inside the hook instead of propagating them.
  3. If a plugin's `.plugin()` call fails, verify the plugin's runtime requirements (config in tauri.conf.json, external binaries, permissions).
  4. Test setup logic in isolation (e.g. open the DB/file before `tauri::Builder::run`) so failures surface with clearer errors.

Example fix

// before
.setup(|app| {
  let win = WebviewWindowBuilder::new(app, "main", WebviewUrl::App("/".into())).build()?;
  Ok(())
})
// after
.setup(|app| {
  match WebviewWindowBuilder::new(app, "main", WebviewUrl::App("/".into())).build() {
    Ok(_) => Ok(()),
    Err(e) => { eprintln!("window setup failed: {e}"); Ok(()) } // or fix the root cause
  }
})
Defensive patterns

Strategy: try-catch

Validate before calling

// validate fallible resources before app.run
let db = Database::open().expect("db"); // fail early with a clear message, not inside setup

Try / catch

.setup(|app| {
  if let Err(e) = risky_initialization() {
    eprintln!("setup warning: {e}"); // recover or decide
    // return Ok(()) to keep running, or propagate to fail fast
  }
  Ok(())
})
// Note: errors returned from setup panic in run(); handle recoverable ones inside the hook

Prevention

When it happens

Trigger: The closure returned by `Builder::build(...).run(callback)` receives `RuntimeRunEvent::Ready` and calls `setup(&mut self)`, which invokes the developer's `.setup(|app| ...)` hook; any `Err` returned from that hook (or from internal setup like plugin initialization, state management, or window creation) triggers the panic.

Common situations: A plugin fails to initialize during `.plugin()` registration; the setup hook returns Err after a failed file/db open; creating a window/webview with an invalid URL or label inside setup; missing single-instance resources in dev.

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 tauri-apps/tauri@460ec35447 (2026-09-18). Data as JSON: /api/errors/b86d832d44002e7d. Report an issue: GitHub.

Appendix: source

Thrown at crates/tauri/src/app.rs:1431

  /// ```
  pub fn run_return<F: FnMut(&AppHandle<R>, RunEvent) + 'static>(mut self, callback: F) -> i32 {
    self.handle.event_loop.lock().unwrap().main_thread_id = std::thread::current().id();

    self
      .runtime
      .take()
      .unwrap()
      .run_return(self.make_run_event_loop_callback(callback))
  }

  fn make_run_event_loop_callback<F: FnMut(&AppHandle<R>, RunEvent) + 'static>(
    mut self,
    mut callback: F,
  ) -> impl FnMut(RuntimeRunEvent<EventLoopMessage>) {
    move |event| match event {
      RuntimeRunEvent::Ready => {
        if let Err(e) = setup(&mut self) {
          panic!("Failed to setup app: {e}");
        }
        let event = on_event_loop_event(self.handle(), RuntimeRunEvent::Ready, self.manager());
        callback(self.handle(), event);
      }
      RuntimeRunEvent::Exit => {
        let event = on_event_loop_event(self.handle(), RuntimeRunEvent::Exit, self.manager());
        callback(self.handle(), event);
        self.cleanup_before_exit();
        if self.manager.restart_on_exit.load(atomic::Ordering::Relaxed) {
          crate::process::restart(&self.env());
        }
      }
      _ => {
        let event = on_event_loop_event(self.handle(), event, self.manager());
        callback(self.handle(), event);
      }
    }
  }

View on GitHub (pinned to 460ec35447)