zeroclaw-labs/zeroclaw · error

{entry_path}.tool duplicates the rule for tool '{tool}'

Error message

{entry_path}.tool duplicates the rule for tool '{tool}'

What it means

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.

Source

Thrown at crates/zeroclaw-config/src/schema.rs:15542

                        anyhow::bail!(
                            "{entry_path}.default_base duplicates the list's default_base entry"
                        );
                    }
                    saw_defaults = true;
                }
                StreamToolArgumentEntry::Tool {
                    tool,
                    include,
                    exclude,
                    ..
                } => {
                    if tool.is_empty() || tool.trim() != tool {
                        anyhow::bail!(
                            "{entry_path}.tool must be a non-empty exact tool name without surrounding whitespace"
                        );
                    }
                    if !tools.insert(tool.as_str()) {
                        anyhow::bail!("{entry_path}.tool duplicates the rule for tool '{tool}'");
                    }

                    let included =
                        validate_stream_tool_argument_names(&entry_path, "include", include)?;
                    let excluded =
                        validate_stream_tool_argument_names(&entry_path, "exclude", exclude)?;
                    for field in &excluded {
                        if included.contains(*field) {
                            anyhow::bail!(
                                "{entry_path} includes and excludes the same argument '{field}'"
                            );
                        }
                    }
                }
            }
        }

        Ok(())

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Merge the two rules into a single entry: combine `base`, `include`, and `exclude` into one rule for that tool.
  2. If one entry is stale, delete it and keep the current one.
  3. While editing, also keep at most one `default_base` entry — it is subject to the same duplicate rejection.

Example fix

# before
stream_tool_arguments = [
  { tool = "delegate", include = ["agent", "prompt"] },
  { tool = "delegate", exclude = ["token"] },
]

# after
stream_tool_arguments = [
  { tool = "delegate", include = ["agent", "prompt"], exclude = ["token"] },
]
Defensive patterns

Strategy: validation

Validate before calling

let mut seen = std::collections::HashSet::new();
for e in &matrix.stream_tool_arguments {
    if let StreamToolArgumentEntry::Tool { tool, .. } = e {
        if !seen.insert(tool.clone()) {
            anyhow::bail!("duplicate rule for tool '{tool}' — merge the entries");
        }
    }
}

Type guard

fn has_unique_tool_rules(cfg: &MatrixConfig) -> bool {
    let mut seen = std::collections::HashSet::new();
    cfg.stream_tool_arguments.iter().all(|e| match e {
        StreamToolArgumentEntry::Tool { tool, .. } => seen.insert(tool.as_str()),
        StreamToolArgumentEntry::Defaults { .. } => true,
    })
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/630d8f4a61164c7c. Report an issue: GitHub.