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

Unknown stash action: {action}. Use: push, pop, list, drop

Error message

Unknown stash action: {action}. Use: push, pop, list, drop

What it means

The git stash tool reads the optional 'action' string (defaulting to "push") and dispatches on it: "push" and "save" run stash push (with optional message, keep_index, include_untracked, paths), "pop", "list", and "drop" (with an 'index' number) map to their git counterparts. Any other value falls through to the bail 'Unknown stash action: {action}. Use: push, pop, list, drop'. The match is case-sensitive and the action must be a JSON string. Note the accepted alias "save" is not listed in the error text.

Source

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

            "drop" => {
                let index_raw = args.get("index").and_then(|v| v.as_u64()).unwrap_or(0);
                let index = i32::try_from(index_raw).map_err(|_| {
                    ::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!({"index": index_raw})),
                        "git_operations: stash index too large"
                    );
                    anyhow::Error::msg(format!("stash index too large: {index_raw}"))
                })?;
                self.run_git_command(
                    &["stash", "drop", &format!("stash@{{{index}}}")],
                    working_dir,
                )
                .await
            }
            _ => anyhow::bail!("Unknown stash action: {action}. Use: push, pop, list, drop"),
        };

        match output {
            Ok(out) => Ok(ToolResult {
                success: true,
                output: out.into(),
                error: None,
            }),
            Err(e) => Ok(ToolResult {
                success: false,
                output: ToolOutput::default(),
                error: Some(format!("Stash {action} failed: {e}")),
            }),
        }
    }

    fn parse_worktree_list(&self, output: &str) -> serde_json::Value {
        let mut worktrees = Vec::new();

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Use one of the four supported actions: push, pop, list, or drop (lowercase, exact).
  2. For 'apply' semantics, use "pop" — it restores the stash and drops it, which is the closest supported behavior (there is no restore-without-drop here).
  3. To inspect stashes, use "list" and read the output instead of "show".
  4. Omit 'action' entirely when you want the default "push" behavior.

Example fix

// before
let args = serde_json::json!({ "action": "apply", "index": 0 });
// tool bails: Unknown stash action: apply. Use: push, pop, list, drop

// after: apply is not exposed; pop restores and drops the entry
let args = serde_json::json!({ "action": "pop" });
Defensive patterns

Strategy: type-guard

Validate before calling

fn build_stash_args(action: &str) -> Option<serde_json::Value> {
    is_supported_stash_action(action).then(|| serde_json::json!({ "action": action }))
}

Type guard

const STASH_ACTIONS: &[&str] = &["push", "pop", "list", "drop"];
fn is_supported_stash_action(action: &str) -> bool {
    STASH_ACTIONS.contains(&action)
}

Try / catch

match tool_result {
    Err(e) if e.to_string().starts_with("Unknown stash action") => {
        // map the requested action to the closest supported one (apply -> pop) or reject
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling stash with {"action": "apply"} (git supports it, this tool does not), {"action": "show"}, {"action": "branch"}, {"action": "clear"}, or a case typo like {"action": "Push"}. Also any non-string JSON value for 'action' would fail the as_str() and fall back to the default, so numeric actions never reach this error.

Common situations: A developer ports a shell workflow that used `git stash apply` or `git stash show` and assumes full git subcommand coverage; an agent guesses the action vocabulary from git's CLI instead of the tool's schema; case mismatch from user input passed through unnormalized.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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