tauri-apps/tauri · error · syn::Error

unable to use self as a command function parameter

Error message

unable to use self as a command function parameter

What it means

#[tauri::command] rewrites the function into a wrapper that extracts each argument by key from the IPC payload. A method receiver (FnArg::Receiver — &self, &mut self, self) has no argument key and no serialization story, so the macro rejects it at compile time with 'unable to use self as a command function parameter', spanned on the receiver.

Source

Thrown at crates/tauri-macros/src/command/wrapper.rs:470

      )
    })
    .collect()
}

/// Transform a [`FnArg`] into a command argument.
fn parse_arg(
  plugin_name: &TokenStream2,
  command: &Ident,
  arg: &FnArg,
  message: &Ident,
  acl: &Ident,
  attributes: &WrapperAttributes,
) -> syn::Result<TokenStream2> {
  // we have no use for self arguments
  let mut arg = match arg {
    FnArg::Typed(arg) => arg.pat.as_ref().clone(),
    FnArg::Receiver(arg) => {
      return Err(syn::Error::new(
        arg.span(),
        "unable to use self as a command function parameter",
      ))
    }
  };

  // we only support patterns that allow us to extract some sort of keyed identifier
  let mut key = match &mut arg {
    Pat::Ident(arg) => arg.ident.unraw().to_string(),
    Pat::Wild(_) => "".into(), // we always convert to camelCase, so "_" will end up empty anyways
    Pat::Struct(s) => super::path_to_command(&mut s.path).ident.to_string(),
    Pat::TupleStruct(s) => super::path_to_command(&mut s.path).ident.to_string(),
    err => {
      return Err(syn::Error::new(
        err.span(),
        "only named, wildcard, struct, and tuple struct arguments allowed",
      ))
    }

View on GitHub (pinned to 52e4b6e71d)

Solutions

  1. Turn the method into a free function and pull dependencies from parameters: state: tauri::State<'_, T>, app: tauri::AppHandle, window: tauri::Window
  2. Register the struct once with .manage(MyStruct { ... }) so State<T> resolves
  3. Keep impl methods for internal logic and have thin free-function commands delegate to them

Example fix

// before
struct Store { items: Vec<String> }
impl Store {
  #[tauri::command]
  fn add(&self, item: String) { self.items.push(item); }
}
// after
struct Store { items: Vec<String> }
#[tauri::command]
fn add(item: String, store: tauri::State<'_, Store>) { store.items.push(item); }
// setup: .manage(Store { items: vec![] })
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Annotating a method inside an impl block: #[tauri::command] fn save(&self, path: String). The generated wrapper cannot forward self across the invoke boundary, so expansion fails.

Common situations: Moving business logic into structs and expecting commands to work as methods; porting an OOP API to tauri commands; forgetting that commands are free functions registered on the builder.

Related errors


AI-assisted analysis of tauri-apps/tauri@52e4b6e71d (2026-08-20). Data as JSON: /api/errors/7e1dd28fe2e27b0d. Report an issue: GitHub.