sigoden/aichat · error

The call ' ' has invalid arguments

Error message

The call '{call_name}' has invalid arguments: {}

What it means

`ToolCall::eval` could not parse the `arguments` field of a tool call as valid JSON. The library expects tool-call arguments to be a JSON object/string containing JSON; anything else is rejected with the raw offending value in the message.

Solutions

  1. Inspect the `{arguments}` value in the message and fix the tool schema/prompt so the model emits valid JSON
  2. Switch to a stronger model that reliably emits JSON tool arguments
  3. Add JSON-repair preprocessing before eval if you control the pipeline

Example fix

// before (model output)
{"arguments": "{name: 'foo'}"}
// after (valid JSON)
{"arguments": "{\"name\": \"foo\"}"}
Defensive patterns

Strategy: validation

Validate before calling

serde_json::from_str::<serde_json::Value>(&call.arguments)
    .map_err(|e| anyhow!("invalid tool arguments: {e}"))?;

Try / catch

match tool_call.eval(&config) {
    Err(e) if e.to_string().contains("has invalid arguments") => {
        eprintln!("Model emitted malformed JSON args; retry with stricter schema");
    }
    other => other?,
}

Prevention

When it happens

Trigger: `self.arguments` is a string that fails `serde_json::from_str`, or arguments are neither object nor string (the `bail!` branch), when `eval` runs.

Common situations: A model emits truncated or single-quoted pseudo-JSON arguments; a weak/local model emits arguments as plain text; upstream API returns arguments in an unexpected format.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09). Data as JSON: /api/errors/de04d7988ff00964. Report an issue: GitHub.

Appendix: source

Thrown at src/function.rs:187

            id,
        }
    }

    pub fn eval(&self, config: &GlobalConfig) -> Result<Value> {
        let (call_name, cmd_name, mut cmd_args, envs) = match &config.read().agent {
            Some(agent) => self.extract_call_config_from_agent(config, agent)?,
            None => self.extract_call_config_from_config(config)?,
        };

        let json_data = if self.arguments.is_object() {
            self.arguments.clone()
        } else if let Some(arguments) = self.arguments.as_str() {
            let arguments: Value = serde_json::from_str(arguments).map_err(|_| {
                anyhow!("The call '{call_name}' has invalid arguments: {arguments}")
            })?;
            arguments
        } else {
            bail!(
                "The call '{call_name}' has invalid arguments: {}",
                self.arguments
            );
        };

        cmd_args.push(json_data.to_string());

        let output = match run_llm_function(cmd_name, cmd_args, envs)? {
            Some(contents) => serde_json::from_str(&contents)
                .ok()
                .unwrap_or_else(|| json!({"output": contents})),
            None => Value::Null,
        };

        Ok(output)
    }

    fn extract_call_config_from_agent(

View on GitHub (pinned to 82976d349a)