zeroclaw-labs/zeroclaw · error · anyhow::Error

No paths to stage

Error message

No paths to stage

What it means

The git add tool requires a 'paths' string and runs it through sanitize_git_args, which both rejects injection patterns and splits the whitespace-separated pathspec string into individual arguments. If sanitization yields zero tokens (sanitized.is_empty()), the tool bails with 'No paths to stage' before running `git add`. The guard prevents an argument-less `git add` invocation, which would either error or, depending on flags, stage nothing meaningful. A missing 'paths' key is a separate error ('Missing 'paths' parameter'); this one means the key existed but contained no usable pathspec tokens.

Source

Thrown at crates/zeroclaw-tools/src/git_operations.rs:551

        working_dir: &std::path::Path,
    ) -> anyhow::Result<ToolResult> {
        let paths = args.get("paths").and_then(|v| v.as_str()).ok_or_else(|| {
            ::zeroclaw_log::record!(
                WARN,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({"param": "paths"})),
                "git_operations: missing paths parameter"
            );
            anyhow::Error::msg("Missing 'paths' parameter")
        })?;

        // Validate paths against injection patterns. Returns each
        // whitespace-separated pathspec as its own argument so the join is
        // not handed to git as a single literal path.
        let sanitized = self.sanitize_git_args(paths)?;
        if sanitized.is_empty() {
            anyhow::bail!("No paths to stage");
        }

        let mut git_args: Vec<&str> = vec!["add", "--"];
        git_args.extend(sanitized.iter().map(String::as_str));

        let output = self.run_git_command(&git_args, working_dir).await;

        match output {
            Ok(_) => Ok(ToolResult {
                success: true,
                output: format!("Staged: {}", sanitized.join(" ")).into(),
                error: None,
            }),
            Err(e) => Ok(ToolResult {
                success: false,
                output: ToolOutput::default(),
                error: Some(format!("Add failed: {e}")),
            }),

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Pass at least one real pathspec, e.g. {"paths": "src/main.rs"} or a directory like {"paths": "crates/zeroclaw-tools"}.
  2. To stage everything in the working tree, pass "." explicitly: {"paths": "."} — the tool appends it after `add --` so it is treated as a literal path.
  3. If paths come from a computed list, skip the add call entirely when the list is empty rather than invoking the tool.
  4. Check for accidental whitespace-only strings from templating or env substitution before calling.

Example fix

// before
let args = serde_json::json!({ "paths": "" });
// tool bails: No paths to stage

// after
let args = serde_json::json!({ "paths": "." }); // stage all changes
Defensive patterns

Strategy: validation

Validate before calling

fn build_add_args(paths: &[String]) -> Option<serde_json::Value> {
    (!paths.is_empty()).then(|| serde_json::json!({ "paths": paths.join(" ") }))
}

Try / catch

match tool_result {
    Err(e) if e.to_string().contains("No paths to stage") => {
        // nothing to stage: treat as a no-op success, not a failure
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling git add with {"paths": ""} or {"paths": " "} — a string with no whitespace-separated tokens. Also occurs when a caller forwards a joined list that turned out empty, e.g. format!("{}", changed_files.join(" ")) on an empty vec.

Common situations: A pipeline computes changed files programmatically and passes an empty join when nothing changed; an agent calls add before any edits exist; a wrapper defaults paths to an empty string instead of "."; a path list variable is unset in config.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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