{"record":{"id":"22f72cd50d9b8ccd","repo":"gitbutlerapp/gitbutler","slug":"tool-name-not-found","errorCode":null,"errorMessage":"Tool '{name}' not found","messagePattern":"Tool '(.+?)' not found","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-tools/src/tool.rs","lineNumber":41,"sourceCode":"}\n\nimpl<'a> WorkspaceToolset<'a> {\n    pub fn new(ctx: &'a mut Context) -> Self {\n        WorkspaceToolset {\n            ctx,\n            tools: BTreeMap::new(),\n            commit_mapping: HashMap::new(),\n        }\n    }\n\n    fn call_tool_inner(\n        &mut self,\n        name: &str,\n        parameters: &str,\n    ) -> anyhow::Result<serde_json::Value> {\n        let tool = self\n            .get(name)\n            .ok_or_else(|| anyhow::anyhow!(\"Tool '{name}' not found\"))?;\n        let params: serde_json::Value = serde_json::from_str(parameters)\n            .map_err(|e| anyhow::anyhow!(\"Failed to parse parameters: {e}\"))?;\n        tool.call(params, self.ctx, &mut self.commit_mapping)\n    }\n}\n\nimpl Toolset for WorkspaceToolset<'_> {\n    fn register_tool<T: Tool>(&mut self, tool: T) {\n        self.tools.insert(tool.name(), Arc::new(tool));\n    }\n\n    fn get(&self, name: &str) -> Option<Arc<dyn Tool>> {\n        self.tools.get(name).cloned()\n    }\n\n    fn list(&self) -> Vec<Arc<dyn Tool>> {\n        self.tools.values().cloned().collect()\n    }","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-tools/src/tool.rs#L23-L59","documentation":"WorkspaceToolset::call_tool_inner looked up the requested tool name in its BTreeMap of registered tools and found nothing. The toolset only contains tools explicitly registered via register_tool, so the caller (usually an LLM tool-call loop or SDK consumer) used a name that was never registered on this instance.","triggerScenarios":"Calling toolset.call_tool(\"get_workspace\", ...) on a WorkspaceToolset built without registering that tool; an LLM hallucinating a tool name not in the registered set; version skew where a tool was renamed or not yet registered.","commonSituations":"Agent/LLM integration invokes a tool from a different toolset version; a consumer copies a tool name from docs but forgets to call register_tool for it first.","solutions":["Call toolset.list() and use only returned tool names when issuing calls","Register the missing tool on the WorkspaceToolset before calling it","Check for renames between versions of but-tools if the name worked before"],"exampleFix":"// before\ntoolset.call_tool(\"list_branches\", \"{}\");\n// after — discover registered names first\nlet names: Vec<String> = toolset.list().iter().map(|t| t.name()).collect();\nassert!(names.contains(&\"list_branches\".to_string()), \"available: {names:?}\");\ntoolset.call_tool(\"list_branches\", \"{}\");","handlingStrategy":"validation","validationCode":"let available: std::collections::HashSet<String> =\n    toolset.list().iter().map(|t| t.name()).collect();\nif !available.contains(requested_name) {\n    return Err(anyhow!(\"tool '{requested_name}' not registered; available: {available:?}\"));\n}","typeGuard":"fn is_registered(toolset: &WorkspaceToolset<'_>, name: &str) -> bool {\n    toolset.get(name).is_some()\n}","tryCatchPattern":"match toolset.call_tool_inner(name, params) {\n    Ok(v) => v,\n    Err(e) if e.to_string().contains(\"Tool '\") && e.to_string().contains(\"not found\") => {\n        // restrict LLM tool list to toolset.list() output and re-prompt\n        json!({\"error\": e.to_string(), \"available\": toolset.list().iter().map(|t| t.name()).collect::<Vec<_>>()})\n    }\n    Err(e) => return json!({\"error\": e.to_string()}),\n}","preventionTips":["Always advertise tools to models from toolset.list(), never from a hardcoded list","Register all tools immediately after WorkspaceToolset::new","Return the available-tool list in error payloads so callers self-correct"],"tags":["rust","tool-calling","llm-tools","unknown-tool","but-tools"],"backgroundTag":"unknown-tool-invocation","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}