{"record":{"id":"3a79f432327127d9","repo":"sigoden/dufs","slug":"invalid-auth-no-duplicate-anonymous-rules","errorCode":null,"errorMessage":"Invalid auth, no duplicate anonymous rules","messagePattern":"Invalid auth, no duplicate anonymous rules","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/auth.rs","lineNumber":65,"sourceCode":"        }\n    }\n}\n\nimpl AccessControl {\n    pub fn new(raw_rules: &[&str]) -> Result<Self> {\n        if raw_rules.is_empty() {\n            return Ok(Self::default());\n        }\n        let new_raw_rules = split_rules(raw_rules);\n        let mut use_hashed_password = false;\n        let mut annoy_paths = None;\n        let mut account_paths_pairs = vec![];\n        for rule in &new_raw_rules {\n            let (account, paths) =\n                split_account_paths(rule).ok_or_else(|| anyhow!(\"Invalid auth `{rule}`\"))?;\n            if account.is_empty() {\n                if annoy_paths.is_some() {\n                    bail!(\"Invalid auth, no duplicate anonymous rules\");\n                }\n                annoy_paths = Some(paths)\n            } else if let Some((user, pass)) = account.split_once(':') {\n                if user.is_empty() || pass.is_empty() {\n                    bail!(\"Invalid auth `{rule}`\");\n                }\n                account_paths_pairs.push((user, pass, paths));\n            }\n        }\n        let mut anonymous = None;\n        if let Some(paths) = annoy_paths {\n            let mut access_paths = AccessPaths::default();\n            access_paths\n                .merge(paths)\n                .ok_or_else(|| anyhow!(\"Invalid auth value `@{paths}\"))?;\n            anonymous = Some(access_paths);\n        }\n        let mut users = IndexMap::new();","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/sigoden/dufs/blob/fe7fd564f80dfbac361c8e0589c3845638149d38/src/auth.rs#L47-L83","documentation":"Auth::new in src/auth.rs parses the comma/semicolon-separated --auth rule list. An empty account (rule with no user:pass before '@') registers anonymous access; registering anonymous access twice would make the ACL ambiguous, so it bails with 'Invalid auth, no duplicate anonymous rules'.","triggerScenarios":"Passing two anonymous rules in --auth, e.g. --auth \"@/:rw,@/public:r\" — the second rule with an empty account triggers the bail because annoy_paths is already Some.","commonSituations":"Trying to grant different permissions to anonymous users on different paths; generated auth strings that accidentally contain an empty account segment; misunderstanding that anonymous access is all-or-nothing in dufs.","solutions":["Keep at most one rule without a user:pass (anonymous) in --auth","Give every additional rule an explicit account, e.g. user:pass@/:rw","Merge anonymous paths into the single anonymous rule or split into multiple dufs instances","Reorder so anonymous rule appears once and protected rules use accounts"],"exampleFix":"# before\ndufs --auth \"@/:rw,@/public:r\"\n# after\ndufs --auth \"@/:r,admin:pass@/:rw\"","handlingStrategy":"validation","validationCode":"# ensure at most one anonymous (no user:pass) rule\nANON_COUNT=$(echo \"$AUTH\" | tr ',' '\\n' | grep -cv '@.*@\\|^[^@]*:.*@')\n# count rules lacking user:pass before '@':\nanon=$(echo \"$AUTH\" | tr ',;' '\\n' | grep -c '^@'); [ \"$anon\" -le 1 ] || { echo \"duplicate anonymous rules\"; exit 1; }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use at most one rule without user:pass","Give all other rules explicit user:pass@path form","Validate the auth string in your deployment tooling before launching dufs"],"tags":["cli","auth","rust","configuration","acl"],"backgroundTag":"conflicting-config-options","analyzedSha":"fe7fd564f80dfbac361c8e0589c3845638149d38","analyzedAt":"2026-09-09T13:01:22.843Z","contentChangedAt":"2026-09-09T13:01:22.843Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}