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
- Confirm the `Commands::WorkflowTool` variant still exists with `#[command(name = "workflow-tool")]` in the Commands enum (crates/tui/src/lib.rs:328).
- Print `cli.command` in the else branch (`panic!("expected workflow-tool command, got {:?}", cli.command)`) to see what actually parsed.
- 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.
- 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
- Keep hidden subcommand `#[command(name = ...)]` renames in sync with tests that reference them.
- Give let-else panics `got {:?}` context so failures self-diagnose.
- Run the focused test filter after every change to the Commands enum or Cli struct.
- Avoid new top-level multi-value args that can swallow subcommand positions.
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
- expected exec command
- expected review command
- cached read tool executes
- continue_goal with a wire-supplied schedule id still parses
- expected doctor command
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)