tauri-apps/tauri · error
state() called before manage() for
Error message
state() called before manage() for {} What it means
The `state<T>()` accessor on Manager/App/AppHandle/Window retrieves state previously registered with `manage::<T>()`. Tauri panics with the type name when no such state exists, because accessing unmanaged state is a programming error; `try_state` is the non-panicking alternative.
Solutions
- Call `app.manage(MyType::new(...))` in `Builder::setup` before any code accesses `state::<MyType>()`.
- Use `app.try_state::<T>()` and handle the None case gracefully instead of panicking.
- Ensure the exact type matches: `state::<Arc<Config>>()` vs `state::<Config>()` must match what was passed to manage().
- If state is produced asynchronously, block/gate access (e.g. Option in a Mutex managed upfront) rather than deferring manage().
Example fix
// before
#[tauri::command]
fn get_count(app: AppHandle) -> i32 { app.state::<Counter>().0 } // panics if never managed
// after
#[tauri::command]
fn get_count(app: AppHandle) -> i32 {
app.try_state::<Counter>().map(|c| c.0).unwrap_or(0)
}
// and in setup:
.setup(|app| { app.manage(Counter(0)); Ok(()) }) Defensive patterns
Strategy: fallback
Validate before calling
if app.try_state::<Counter>().is_none() {
app.manage(Counter(0));
}
let counter = app.state::<Counter>(); // safe now Try / catch
// if using try_state in a command
let counter = app.try_state::<Counter>()
.ok_or_else(|| tauri::Error::Anyhow(anyhow::anyhow!("state not initialized")))?; Prevention
- Manage all state inside Builder::setup before run()
- Always request the exact type passed to manage() (watch Arc<T> vs T)
- Prefer try_state().ok_or(...) in commands to return errors, not panic
- Never defer manage() behind async work that commands may race with
When it happens
Trigger: Calling `app.state::<T>()` (or handle/window.state) before `app.manage(T)` was called, or for a type that was never managed (different concrete type, e.g. managing `Config` but requesting `MyConfig`, or managing `Arc<Config>` vs `Config` mismatch).
Common situations: Accessing state from a command or event handler that runs before setup completes; a refactor renaming the state type; double-managed confusion where a wrapper type was stored instead of the inner type.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- invalid permission identifier
- missing command scope for key
- Not a CheckMenuItem
- Not a MenuItem
- Not a PredefinedMenuItem
AI-assisted analysis of tauri-apps/tauri@460ec35447 (2026-09-18).
Data as JSON: /api/errors/e931b1217b663e86.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tauri/src/lib.rs:734
where
T: Send + Sync + 'static,
{
// The caller decides to break the safety here, then OK, just let it go.
unsafe { self.manager().state.unmanage() }
}
/// Retrieves the managed state for the type `T`.
///
/// # Panics
///
/// Panics if the state for the type `T` has not been previously [managed](Self::manage).
/// Use [try_state](Self::try_state) for a non-panicking version.
fn state<T>(&self) -> State<'_, T>
where
T: Send + Sync + 'static,
{
self.manager().state.try_get().unwrap_or_else(|| {
panic!(
"state() called before manage() for {}",
std::any::type_name::<T>()
)
})
}
/// Attempts to retrieve the managed state for the type `T`.
///
/// Returns `Some` if the state has previously been [managed](Self::manage). Otherwise returns `None`.
fn try_state<T>(&self) -> Option<State<'_, T>>
where
T: Send + Sync + 'static,
{
self.manager().state.try_get()
}
/// Get a reference to the resources table of this manager.
fn resources_table(&self) -> MutexGuard<'_, ResourceTable>;View on GitHub (pinned to 460ec35447)