Hmbown/CodeWhale · error
--input-json must be a JSON object
Error message
--input-json must be a JSON object
What it means
After passing the approval-source check, `workflow-tool` parses `--input-json` with serde_json and additionally requires the parsed value to be a JSON object (`input.is_object()`). Non-object JSON (arrays, strings, numbers) is rejected even though it is valid JSON.
Solutions
- Wrap the payload in a JSON object, e.g. `{"action": "run", ...}`.
- Quote the whole `--input-json` value in single quotes in the shell to survive quoting.
- Validate with `echo '<payload>' | jq -e 'type == "object"'` before invoking.
Example fix
// before
codewhale workflow-tool --input-json '[{"action":"run"}]'
// after
codewhale workflow-tool --approval-source explicit-workflow-command --input-json '{"action":"run"}' Defensive patterns
Strategy: validation
Validate before calling
const input = JSON.parse(inputJson);
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
throw new Error('--input-json must be a JSON object');
}
Type guard
function isJsonObject(v) {
return typeof v === 'object' && v !== null && !Array.isArray(v);
}
Try / catch
match serde_json::from_str::<serde_json::Value>(&args.input_json) {
Ok(v) if v.is_object() => run(v),
Ok(_) => bail!("--input-json must be a JSON object"),
Err(e) => bail!("invalid JSON: {e}"),
}
Prevention
- Validate payloads with `jq -e 'type == "object"'` before invoking.
- Single-quote the whole --input-json value in shells to survive quoting.
- Always model workflow input as a top-level object with an `action` field.
When it happens
Trigger: Passing `--input-json` that is a JSON array, scalar, or null — or unparseable text, which fails earlier with the `.context(...)` message — so `is_object()` returns false.
Common situations: Shell quoting mangling the JSON (quotes stripped so `{action:"run"}` is not valid JSON), or passing a top-level array of actions instead of a single object.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- DSH profile must be `web`, `headless`, or
- invalid request
- missing
- Model response incomplete: provider stop reason
- Pass a host like `github.com`, not a URL path
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/7198d76cc57f13f4.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/lib.rs:12229
exit_workflow_tool_failure();
}
}
}
async fn run_workflow_tool_command_inner(
cli: &Cli,
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());View on GitHub (pinned to 73e0f67d83)