Hmbown/CodeWhale · error

expected workflow-tool command

Error message

expected workflow-tool command

What it means

A test-only panic in crates/tui/src/lib.rs:14972. The test calls parse_cli (a clap Cli::try_parse wrapper around the codewhale-tui CLI) and uses a let-else to destructure `Commands::WorkflowTool(args)`; if the parsed command is `None` or any other variant, the else branch panics with "expected workflow-tool command". It signals that the `workflow-tool` subcommand was not recognized or was shadowed by top-level flags.

Solutions

  1. Confirm the `Commands::WorkflowTool` variant still exists with `#[command(name = "workflow-tool")]` in the Commands enum (crates/tui/src/lib.rs:328).
  2. Print `cli.command` in the else branch (`panic!("expected workflow-tool command, got {:?}", cli.command)`) to see what actually parsed.
  3. Check whether new top-level flags (e.g. `--prompt` with num_args=1..) are intercepting the subcommand position; move them after the subcommand or mark them with `trailing_var_arg` semantics.
  4. Update the test argv if the subcommand name was intentionally renamed.

Example fix

// before
let Some(Commands::WorkflowTool(args)) = cli.command else {
    panic!("expected workflow-tool command");
};
// after
let Some(Commands::WorkflowTool(args)) = cli.command else {
    panic!("expected workflow-tool command, got {:?}", cli.command);
};
Defensive patterns

Strategy: validation

Validate before calling

// Before asserting, check the variant explicitly:
match cli.command {
    Some(Commands::WorkflowTool(ref args)) => assert!(args.input_json.contains("\"action\":\"run\"")),
    other => panic!("expected workflow-tool command, got {other:?}"),
}

Type guard

fn as_workflow_tool(cmd: &Option<Commands>) -> Option<&WorkflowToolArgs> {
    match cmd { Some(Commands::WorkflowTool(a)) => Some(a), _ => None }
}

Prevention

When it happens

Trigger: parse_cli(&["codewhale","workflow-tool","--approval-source","explicit-workflow-command","--input-json","{...}"]) returns a Cli whose `command` is not `Commands::WorkflowTool` — typically because the subcommand name changed, the hidden `#[command(name = "workflow-tool")]` rename was removed, or a global/flatten arg consumed the tokens so the subcommand parsed as None.

Common situations: Renaming the Commands enum variant or its clap `name` without updating tests; adding a top-level multi-value arg (like `prompt: Vec<String>` with num_args=1..) that swallows the subcommand; running the test after a conflict in FeatureToggles flatten flags.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/39f79c6e589c23e3. Report an issue: GitHub.

Appendix: source

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

            cli.command,
            Some(Commands::Auth(TuiAuthArgs {
                command: TuiAuthCommand::ChatgptRevoke
            }))
        ));
    }

    #[test]
    fn workflow_tool_internal_subcommand_parses_exact_json() {
        let cli = parse_cli(&[
            "codewhale-tui",
            "workflow-tool",
            "--approval-source",
            "explicit-workflow-command",
            "--input-json",
            r#"{"action":"run","source_path":"workflows/demo.js"}"#,
        ]);
        let Some(Commands::WorkflowTool(args)) = cli.command else {
            panic!("expected workflow-tool command");
        };
        assert!(args.input_json.contains("\"action\":\"run\""));
    }

    #[tokio::test]
    async fn direct_workflow_tool_runs_without_an_operator_model_turn() {
        use crate::tools::spec::ToolSpec;

        let workspace = tempfile::tempdir().expect("workspace");
        let config = Config {
            provider: Some("vllm".to_string()),
            mcp_config_path: Some(
                workspace
                    .path()
                    .join("missing-mcp.json")
                    .display()
                    .to_string(),
            ),

View on GitHub (pinned to 433685b202)