Repository: tauri-apps/tauri (language: rust)

Issue:
[bug] Unmanaged state panics if invoked
### Describe the bug

In dev environments, if the frontend calls a Tauri command that uses `State<'_, T>` in its parameters and does so before `tauri::Manager::manage` gets called to manage the state, then the application will panic.

My frontend needs to perform some utility methods such as writing and reading from files as soon as it can. On many occasions, the frontend starts before the backend can fully catch up.

### Reproduction

Tell the app to manage some state asynchronously:
```rust 
let some_state = SomeState::new();
...
.setup(|app| {
    let app_handle = app.handle().clone();
    tauri::async_runtime::spawn(async move {
        app_handle.manage(some_state);
    }
    Ok(())
})
```

Make a function that uses that state:
```rust
#[tauri::command]
async fn some_command(state: State<'_, SomeState>) -> Result<(), String>
```

Call the command as soon as possible in the frontend. In my case, I am using Angular and thus do so in my `app.component.ts`'s OnInit:
```typescript
async ngOnInit() {
   await invoke<void>("some_command").catch(err => {console.log(err)});
}
```
In a dev environment, as long as the frontend started first, the app should panic.

### Expected behavior

Ideally, functions that return a `Result` should capture the error rather than panicking the whole program, or the user should be able to wrap the state in an `Option` in the parameters, such as:
 `async fn some_command(state: Option<State<'_, SomeState>>) -> Result<(), String>`
and have the function deal with scenarios where the state was not managed yet.

### Full `tauri info` output

```text
[✔] Environment
    - OS: Windows 10.0.22631 x86_64 (X64)
    ✔ WebView2: 131.0.2903.86
    ✔ MSVC:
        - Visual Studio Community 2019
        - Visual Studio Community 2022
    ✔ rustc: 1.83.0 (90b35a623 2024-11-26)
    ✔ cargo: 1.83.0 (5ffbef321 2024-10-29)
    ✔ rustup: 1.27.1 (54dd3d00f 2024-04-24)
    ✔ Rust toolchain: stable-x86_64-pc-windows-msvc (default)
    - node: 20.12.2
    - yarn: 1.22.22
    - npm: 10.5.0

[-] Packages
    - tauri 🦀: 2.1.1
    - tauri-build 🦀: 2.0.3
    - wry 🦀: 0.47.2
    - tao 🦀: 0.30.8
    - tauri-cli 🦀: 2.0.4
    - @tauri-apps/api : 2.0.3
    - @tauri-apps/cli : 2.0.5

[-] Plugins
    - tauri-plugin-shell 🦀: 2.0.2
    - @tauri-apps/plugin-shell : 2.0.1

[-] App
    - build-type: bundle
    - CSP: unset
    - frontendDist: ../dist/desktop-search/browser
    - devUrl: http://localhost:1420/
    - framework: Angular
    - bundler: Webpack
```


### Stack trace

```text
thread 'main' panicked at C:\Users\grays\.cargo\registry\src\index.crates.io-6f17d22bba15001f\tauri-2.1.1\src\state.rs:64:7:
state not managed for field `service` on command `load_json_local`. You must call `.manage()` before using this command
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error Command failed with exit code 4294967295.
```


### Additional context

Please let me know if there is a workaround for this. Thanks.


Discussion / hints:

Fix patch:
diff --git a/.changes/unmanaged-state-command.md b/.changes/unmanaged-state-command.md
new file mode 100644
index 000000000000..fca4ecb6fc2f
--- /dev/null
+++ b/.changes/unmanaged-state-command.md
@@ -0,0 +1,5 @@
+---
+"tauri": "patch:bug"
+---
+
+Fix panic when invoking a command with an unmanaged state, an error will be returned instead.
diff --git a/Cargo.lock b/Cargo.lock
index 173a44c92ff0..a5acd18ef2d7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4858,7 +4858,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
 dependencies = [
  "cfg-if",
- "windows-targets 0.48.5",
+ "windows-targets 0.52.6",
 ]
 
 [[package]]
@@ -6647,9 +6647,9 @@ dependencies = [
 
 [[package]]
 name = "png"
-version = "0.17.15"
+version = "0.17.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b67582bd5b65bdff614270e2ea89a1cf15bef71245cc1e5f7ea126977144211d"
+checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0"
 dependencies = [
  "bitflags 1.3.2",
  "crc32fast",
@@ -7978,9 +7978,9 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
 
 [[package]]
 name = "serde"
-version = "1.0.216"
+version = "1.0.215"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
+checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
 dependencies = [
  "serde_derive",
 ]
@@ -8042,9 +8042,9 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.216"
+version = "1.0.215"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
+checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -9042,7 +9042,7 @@ dependencies = [
  "tauri-runtime",
  "tauri-runtime-wry",
  "tauri-utils 2.1.0",
- "thiserror 2.0.6",
+ "thiserror 2.0.4",
  "tokio",
  "tracing",
  "tray-icon",
@@ -9110,7 +9110,7 @@ dependencies = [
  "tauri-macos-sign",
  "tauri-utils 2.1.0",
  "tempfile",
- "thiserror 2.0.6",
+ "thiserror 2.0.4",
  "time",
  "ureq",
  "url",
@@ -9230,7 +9230,7 @@ dependencies = [
  "sha2",
  "syn 2.0.90",
  "tauri-utils 2.1.0",
- "thiserror 2.0.6",
+ "thiserror 2.0.4",
  "time",
  "url",
  "uuid",
@@ -9369,7 +9369,7 @@ dependencies = [
  "serde",
  "tauri",
  "tauri-plugin 2.0.3",
- "thiserror 2.0.6",
+ "thiserror 2.0.4",
 ]
 
 [[package]]
@@ -9384,7 +9384,7 @@ dependencies = [
  "serde",
  "serde_json",
  "tauri-utils 2.1.0",
- "thiserror 2.0.6",
+ "thiserror 2.0.4",
  "url",
  "windows",
 ]
@@ -9536,7 +9536,7 @@ dependencies = [
  "serial_test",
  "serialize-to-javascript",
  "swift-rs",
- "thiserror 2.0.6",
+ "thiserror 2.0.4",
  "toml 0.8.19",
  "url",
  "urlpattern",
@@ -9637,11 +9637,11 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "2.0.6"
+version = "2.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47"
+checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490"
 dependencies = [
- "thiserror-impl 2.0.6",
+ "thiserror-impl 2.0.4",
 ]
 
 [[package]]
@@ -9657,9 +9657,9 @@ dependencies = [
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.6"
+version = "2.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312"
+checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -10801,7 +10801,7 @@ version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
diff --git a/crates/tauri/src/state.rs b/crates/tauri/src/state.rs
index 8175e62c7272..2f1092e6ce02 100644
--- a/crates/tauri/src/state.rs
+++ b/crates/tauri/src/state.rs
@@ -60,12 +60,12 @@ impl<T: Send + Sync + std::fmt::Debug> std::fmt::Debug for State<'_, T> {
 impl<'r, 'de: 'r, T: Send + Sync + 'static, R: Runtime> CommandArg<'de, R> for State<'r, T> {
   /// Grabs the [`State`] from the [`CommandItem`]. This will never fail.
   fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError> {
-    Ok(command.message.state_ref().try_get().unwrap_or_else(|| {
-      panic!(
+    command.message.state_ref().try_get().ok_or_else(|| {
+      InvokeError::from_anyhow(anyhow::anyhow!(
         "state not managed for field `{}` on command `{}`. You must call `.manage()` before using this command",
         command.key, command.name
-      )
-    }))
+      ))
+    })
   }
 }
 


Test patch: