{"record":{"id":"630d8f4a61164c7c","repo":"zeroclaw-labs/zeroclaw","slug":"entry-path-tool-duplicates-the-rule-for-tool-t","errorCode":null,"errorMessage":"{entry_path}.tool duplicates the rule for tool '{tool}'","messagePattern":"(.+?)\\.tool duplicates the rule for tool '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-config/src/schema.rs","lineNumber":15542,"sourceCode":"                        anyhow::bail!(\n                            \"{entry_path}.default_base duplicates the list's default_base entry\"\n                        );\n                    }\n                    saw_defaults = true;\n                }\n                StreamToolArgumentEntry::Tool {\n                    tool,\n                    include,\n                    exclude,\n                    ..\n                } => {\n                    if tool.is_empty() || tool.trim() != tool {\n                        anyhow::bail!(\n                            \"{entry_path}.tool must be a non-empty exact tool name without surrounding whitespace\"\n                        );\n                    }\n                    if !tools.insert(tool.as_str()) {\n                        anyhow::bail!(\"{entry_path}.tool duplicates the rule for tool '{tool}'\");\n                    }\n\n                    let included =\n                        validate_stream_tool_argument_names(&entry_path, \"include\", include)?;\n                    let excluded =\n                        validate_stream_tool_argument_names(&entry_path, \"exclude\", exclude)?;\n                    for field in &excluded {\n                        if included.contains(*field) {\n                            anyhow::bail!(\n                                \"{entry_path} includes and excludes the same argument '{field}'\"\n                            );\n                        }\n                    }\n                }\n            }\n        }\n\n        Ok(())","sourceCodeStart":15524,"sourceCodeEnd":15560,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-config/src/schema.rs#L15524-L15560","documentation":"The `stream_tool_arguments` list is order-independent: ZeroClaw resolves each tool by exact name, so at most one rule may exist per tool. The validator tracks seen names in a HashSet and rejects a second `{ tool = ... }` entry naming the same tool, because two rules would make the rendered argument set ambiguous.","triggerScenarios":"Two entries with the same `tool` value in one `stream_tool_arguments` list, e.g. two `{ tool = \"delegate\", ... }` rules; merging config snippets (base config plus an override file) that each define a rule for the same tool.","commonSituations":"Appending an override rule for a tool that already has one instead of editing the existing rule; layered configs (site + user) that both tune the same tool; editing a long list and forgetting an earlier entry.","solutions":["Merge the two rules into a single entry: combine `base`, `include`, and `exclude` into one rule for that tool.","If one entry is stale, delete it and keep the current one.","While editing, also keep at most one `default_base` entry — it is subject to the same duplicate rejection."],"exampleFix":"# before\nstream_tool_arguments = [\n  { tool = \"delegate\", include = [\"agent\", \"prompt\"] },\n  { tool = \"delegate\", exclude = [\"token\"] },\n]\n\n# after\nstream_tool_arguments = [\n  { tool = \"delegate\", include = [\"agent\", \"prompt\"], exclude = [\"token\"] },\n]","handlingStrategy":"validation","validationCode":"let mut seen = std::collections::HashSet::new();\nfor e in &matrix.stream_tool_arguments {\n    if let StreamToolArgumentEntry::Tool { tool, .. } = e {\n        if !seen.insert(tool.clone()) {\n            anyhow::bail!(\"duplicate rule for tool '{tool}' — merge the entries\");\n        }\n    }\n}","typeGuard":"fn has_unique_tool_rules(cfg: &MatrixConfig) -> bool {\n    let mut seen = std::collections::HashSet::new();\n    cfg.stream_tool_arguments.iter().all(|e| match e {\n        StreamToolArgumentEntry::Tool { tool, .. } => seen.insert(tool.as_str()),\n        StreamToolArgumentEntry::Defaults { .. } => true,\n    })\n}","tryCatchPattern":null,"preventionTips":["Keep one rule per tool; edit the existing rule instead of appending a second.","When layering config files, grep the merged result for duplicated tool keys.","Treat 'duplicate rule' errors as a prompt to merge semantics, not just delete one."],"tags":["matrix","streaming","config","duplicate","tool-arguments"],"backgroundTag":"duplicate-config-key","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}