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
- Turn the method into a free function and pull dependencies from parameters: state: tauri::State<'_, T>, app: tauri::AppHandle, window: tauri::Window
- Register the struct once with .manage(MyStruct { ... }) so State<T> resolves
- 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
- Write commands as free functions, never methods
- Inject dependencies via tauri::State, AppHandle, Window parameters and .manage()
- Keep methods for internal logic; have command free functions delegate to them
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
- unexpected list input
- expected "camelCase" or "snake_case"
- expected string literal for rename
- unexpected input, expected one of `rename_all`, `rename`, `r
- only named, wildcard, struct, and tuple struct arguments all
AI-assisted analysis of tauri-apps/tauri@52e4b6e71d (2026-08-20).
Data as JSON: /api/errors/7e1dd28fe2e27b0d.
Report an issue: GitHub.