gitbutlerapp/gitbutler · error · anyhow::Error

Failed to parse parameters: {e}

Error message

Failed to parse parameters: {e}

What it means

WorkspaceToolset::call_tool_inner failed to deserialize the `parameters` argument with serde_json::from_str — the caller passed a string that is not valid JSON. Parameters must be a complete JSON document (typically a JSON object), not bare key=value text or a truncated payload.

Source

Thrown at crates/but-tools/src/tool.rs:43

impl<'a> WorkspaceToolset<'a> {
    pub fn new(ctx: &'a mut Context) -> Self {
        WorkspaceToolset {
            ctx,
            tools: BTreeMap::new(),
            commit_mapping: HashMap::new(),
        }
    }

    fn call_tool_inner(
        &mut self,
        name: &str,
        parameters: &str,
    ) -> anyhow::Result<serde_json::Value> {
        let tool = self
            .get(name)
            .ok_or_else(|| anyhow::anyhow!("Tool '{name}' not found"))?;
        let params: serde_json::Value = serde_json::from_str(parameters)
            .map_err(|e| anyhow::anyhow!("Failed to parse parameters: {e}"))?;
        tool.call(params, self.ctx, &mut self.commit_mapping)
    }
}

impl Toolset for WorkspaceToolset<'_> {
    fn register_tool<T: Tool>(&mut self, tool: T) {
        self.tools.insert(tool.name(), Arc::new(tool));
    }

    fn get(&self, name: &str) -> Option<Arc<dyn Tool>> {
        self.tools.get(name).cloned()
    }

    fn list(&self) -> Vec<Arc<dyn Tool>> {
        self.tools.values().cloned().collect()
    }

    fn call_tool(&mut self, name: &str, parameters: &str) -> serde_json::Value {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Serialize parameters with a real JSON serializer (serde_json::to_string, JSON.stringify) instead of hand-building the string
  2. Validate the string parses before calling: parse it as serde_json::Value in a prior step
  3. When consuming results, check for the {"error": "Failed to call tool ..."} key that call_tool returns on failure

Example fix

// before
toolset.call_tool("get_stack", format!("stack_id={}", id)); // not JSON
// after
let params = serde_json::to_string(&serde_json::json!({ "stack_id": id }))?;
toolset.call_tool("get_stack", &params);
Defensive patterns

Strategy: validation

Validate before calling

// validate before calling — parse to Value first
let value: serde_json::Value = serde_json::from_str(params_str)
    .with_context(|| "tool parameters must be valid JSON")?;
assert!(value.is_object(), "tool parameters should be a JSON object");
toolset.call_tool(name, params_str);

Try / catch

when (result.error) {
  if (result.error.includes("Failed to parse parameters")) { /* re-serialize args with JSON.stringify and retry once */ }
}

Prevention

When it happens

Trigger: Calling call_tool(name, "branch: main") or passing a string with smart quotes, trailing commas, single-quoted keys, or a payload truncated by a size limit; note call_tool itself swallows this into an {"error": ...} JSON value rather than panicking.

Common situations: LLM emits malformed JSON for tool arguments; string interpolation builds parameters without json!()/serde serialization; encoding issues (BOM, non-UTF8) corrupt the payload.

Understand the failure class

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/74e7d9f6ce9a76a5. Report an issue: GitHub.