{"record":{"id":"7e1dd28fe2e27b0d","repo":"tauri-apps/tauri","slug":"unable-to-use-self-as-a-command-function-parameter","errorCode":null,"errorMessage":"unable to use self as a command function parameter","messagePattern":"unable to use self as a command function parameter","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"crates/tauri-macros/src/command/wrapper.rs","lineNumber":470,"sourceCode":"      )\n    })\n    .collect()\n}\n\n/// Transform a [`FnArg`] into a command argument.\nfn parse_arg(\n  plugin_name: &TokenStream2,\n  command: &Ident,\n  arg: &FnArg,\n  message: &Ident,\n  acl: &Ident,\n  attributes: &WrapperAttributes,\n) -> syn::Result<TokenStream2> {\n  // we have no use for self arguments\n  let mut arg = match arg {\n    FnArg::Typed(arg) => arg.pat.as_ref().clone(),\n    FnArg::Receiver(arg) => {\n      return Err(syn::Error::new(\n        arg.span(),\n        \"unable to use self as a command function parameter\",\n      ))\n    }\n  };\n\n  // we only support patterns that allow us to extract some sort of keyed identifier\n  let mut key = match &mut arg {\n    Pat::Ident(arg) => arg.ident.unraw().to_string(),\n    Pat::Wild(_) => \"\".into(), // we always convert to camelCase, so \"_\" will end up empty anyways\n    Pat::Struct(s) => super::path_to_command(&mut s.path).ident.to_string(),\n    Pat::TupleStruct(s) => super::path_to_command(&mut s.path).ident.to_string(),\n    err => {\n      return Err(syn::Error::new(\n        err.span(),\n        \"only named, wildcard, struct, and tuple struct arguments allowed\",\n      ))\n    }","sourceCodeStart":452,"sourceCodeEnd":488,"githubUrl":"https://github.com/tauri-apps/tauri/blob/52e4b6e71d8632a7e648f866c442e287ecddee34/crates/tauri-macros/src/command/wrapper.rs#L452-L488","documentation":"#[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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nstruct Store { items: Vec<String> }\nimpl Store {\n  #[tauri::command]\n  fn add(&self, item: String) { self.items.push(item); }\n}\n// after\nstruct Store { items: Vec<String> }\n#[tauri::command]\nfn add(item: String, store: tauri::State<'_, Store>) { store.items.push(item); }\n// setup: .manage(Store { items: vec![] })","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["macros","command","self-parameter","compile-time","state-management"],"backgroundTag":"unsupported-macro-input","analyzedSha":"52e4b6e71d8632a7e648f866c442e287ecddee34","analyzedAt":"2026-08-20T13:59:20.734Z","contentChangedAt":"2026-08-20T13:59:20.734Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}