{"record":{"id":"813060d8b94691ef","repo":"PyO3/pyo3","slug":"get-may-only-be-specified-once","errorCode":null,"errorMessage":"`get` may only be specified once","messagePattern":"`get` may only be specified once","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"pyo3-macros-backend/src/pyclass.rs","lineNumber":402,"sourceCode":"        } else {\n            Err(lookahead.error())\n        }\n    }\n}\n\nimpl FieldPyO3Options {\n    fn take_pyo3_options(attrs: &mut Vec<syn::Attribute>) -> Result<Self> {\n        let mut options = FieldPyO3Options {\n            get: None,\n            set: None,\n            name: None,\n        };\n\n        for option in take_pyo3_options(attrs)? {\n            match option {\n                FieldPyO3Option::Get(kw) => {\n                    if options.get.replace(Annotated::Field(kw)).is_some() {\n                        return Err(syn::Error::new(kw.span(), UNIQUE_GET));\n                    }\n                }\n                FieldPyO3Option::Set(kw) => {\n                    if options.set.replace(Annotated::Field(kw)).is_some() {\n                        return Err(syn::Error::new(kw.span(), UNIQUE_SET));\n                    }\n                }\n                FieldPyO3Option::Name(name) => {\n                    if options.name.replace(name).is_some() {\n                        return Err(syn::Error::new(options.name.span(), UNIQUE_NAME));\n                    }\n                }\n            }\n        }\n\n        Ok(options)\n    }\n}","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/pyo3-macros-backend/src/pyclass.rs#L384-L420","documentation":"pyo3's #[pyclass]/field attribute parser rejects a `get` option that appears more than once on the same item. Each field attribute option is stored in an Option slot; when a duplicate `get` keyword is parsed, the `replace` on the slot returns Some and the macro emits this syn::Error pointing at the duplicate keyword's span.","triggerScenarios":"Compiling Rust code where a #[pyclass] field (or getter) declares `#[pyo3(get)]` (or `get` inside the macro args) twice, e.g. `#[pyo3(get, get)]` or a duplicated option spread across merged attribute lists.","commonSituations":"Copy-paste of attribute lines when adding getters/refactor merges that concatenate two attribute sets; macro-generated code that blindly appends `get` to an existing attribute list.","solutions":["Remove the duplicate `get` keyword so it appears at most once per field","If two attribute lists are being merged, de-duplicate options before passing them to the macro","Check macro-generated or wrapper code that may inject `get` automatically"],"exampleFix":"// before\n#[pyclass]\nstruct Point {\n    #[pyo3(get, get)]\n    x: i32,\n}\n// after\n#[pyclass]\nstruct Point {\n    #[pyo3(get)]\n    x: i32,\n}","handlingStrategy":"validation","validationCode":"// Compile-time check: ensure each pyclass field lists each option once\nfn validate_unique_options(attrs: &[&str]) -> Result<(), String> {\n    let mut seen = std::collections::HashSet::new();\n    for a in attrs {\n        if !seen.insert(a) { return Err(format!(\"duplicate option: {}\", a)); }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["List each `get`/`set`/`name` option once per field","When merging attribute fragments, de-duplicate first","Run cargo check promptly after attribute edits — these fail at compile time"],"tags":["rust","pyo3","macro","compile-time"],"backgroundTag":"duplicate-attribute-option","analyzedSha":"ac9b6899d348be4d54614d060dea53a645a12e36","analyzedAt":"2026-09-05T09:20:35.319Z","contentChangedAt":"2026-09-05T09:20:35.319Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}