Hmbown/CodeWhale · error · anyhow::Error

workflow-tool accepts only action=run

Error message

workflow-tool accepts only action=run

What it means

workflow-tool executes only the run action: after the object shape check, the input's action field must be the string 'run' (case-insensitive). A missing action key or any other action name is refused before any workflow starts.

Source

Thrown at crates/tui/src/lib.rs:10623

    args: WorkflowToolArgs,
    plugin_registry: std::sync::Arc<crate::plugins::PluginRegistry>,
) -> Result<()> {
    use crate::tools::spec::ToolSpec;

    if args.approval_source != "explicit-workflow-command" {
        bail!("workflow-tool requires --approval-source explicit-workflow-command");
    }
    let input: serde_json::Value = serde_json::from_str(&args.input_json)
        .context("--input-json must be a valid Workflow tool input object")?;
    if !input.is_object() {
        bail!("--input-json must be a JSON object");
    }
    if !input
        .get("action")
        .and_then(serde_json::Value::as_str)
        .is_some_and(|action| action.eq_ignore_ascii_case("run"))
    {
        bail!("workflow-tool accepts only action=run");
    }

    let workspace = resolve_workspace(cli);
    let mut config = load_config_from_cli(cli)?;
    merge_user_workspace_config(&mut config, cli.config.clone(), &workspace);
    if let Ok(env_url) =
        std::env::var("CODEWHALE_BASE_URL").or_else(|_| std::env::var("DEEPSEEK_BASE_URL"))
    {
        let trimmed = env_url.trim();
        if !trimmed.is_empty() {
            config.base_url = Some(trimmed.to_string());
        }
    }

    let model = resolve_exec_model(&config, None);
    let route = resolve_cli_exec_route(
        &config,
        &model,

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Set action to "run" plus the fields the run action requires
  2. Use the dedicated workflow commands for status/cancel-style operations
  3. Validate the action key exists and equals run before invoking

Example fix

# before
--input-json '{"action":"status","id":7}'

# after
--input-json '{"action":"run","id":7}'
Defensive patterns

Strategy: validation

Validate before calling

jq -e '((.action // "") | ascii_downcase) == "run"' <<<"$INPUT_JSON" >/dev/null || { echo 'action must be "run"'; exit 1; }

Type guard

const isRunAction = (v) =>
  typeof v?.action === 'string' && v.action.toLowerCase() === 'run';

Prevention

When it happens

Trigger: Passing {"action":"status"} or {"action":"cancel"}; omitting the action field entirely; a model hallucinating action names from a different tool's API.

Common situations: Reusing request shapes from other tool surfaces; stale examples; case variants like 'Run' (accepted) vs 'running' (rejected).

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/9b3a9a2a3f9c07dc. Report an issue: GitHub.