sigoden/aichat · error
No command generated
Error message
No command generated
What it means
Fires in shell_execute after the model's chat completion returns an empty evaluation string. Since the entire purpose of the call is to produce a shell command, an empty response means the LLM failed to generate anything executable, so the flow aborts before running a blank command.
Solutions
- Rephrase the prompt so the model produces an actual command
- Retry the request (transient empty completions)
- Check the provider response — empty completions may indicate an API/model issue
- Verify `after_chat_completion` hooks aren't stripping the output
Example fix
// before shell: "" // after shell: "list all png files larger than 1MB in /tmp"
Defensive patterns
Strategy: validation
Validate before calling
if prompt.trim().is_empty() {
eprintln!("provide a non-empty shell task description");
} Try / catch
match shell_execute(...).await {
Err(e) if e.to_string() == "No command generated" => {
eprintln!("Model returned empty output; retry or rephrase prompt");
}
other => other?,
} Prevention
- Write specific, imperative prompts for shell generation
- Retry once on empty completions (often transient)
- Verify no after_chat_completion hook is stripping the response
When it happens
Trigger: `call_chat_completions` returns an empty string after the after_chat_completion hook — e.g. the model replied with only whitespace or the response was filtered.
Common situations: Model refuses or returns an empty answer for an ambiguous prompt; overly strict system prompt suppresses output; a provider outage yields an empty completion.
Related errors
- Exceed max_input_tokens limit
- The request was aborted because an infinite loop of…
- Tool call exit with
- Invalid document loader
AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09).
Data as JSON: /api/errors/eb6ad4951e4ed309.
Report an issue: GitHub.
Appendix: source
Thrown at src/main.rs:256
}
#[async_recursion::async_recursion]
async fn shell_execute(
config: &GlobalConfig,
shell: &Shell,
mut input: Input,
abort_signal: AbortSignal,
) -> Result<()> {
let client = input.create_client()?;
config.write().before_chat_completion(&input)?;
let (eval_str, _) =
call_chat_completions(&input, false, true, client.as_ref(), abort_signal.clone()).await?;
config
.write()
.after_chat_completion(&input, &eval_str, &[])?;
if eval_str.is_empty() {
bail!("No command generated");
}
if config.read().dry_run {
config.read().print_markdown(&eval_str)?;
return Ok(());
}
if *IS_STDOUT_TERMINAL {
let options = ["execute", "revise", "describe", "copy", "quit"];
let command = color_text(eval_str.trim(), nu_ansi_term::Color::Rgb(255, 165, 0));
let first_letter_color = nu_ansi_term::Color::Cyan;
let prompt_text = options
.iter()
.map(|v| format!("{}{}", color_text(&v[0..1], first_letter_color), &v[1..]))
.collect::<Vec<String>>()
.join(&dimmed_text(" | "));
loop {
println!("{command}");
let answer_char =
read_single_key(&['e', 'r', 'd', 'c', 'q'], 'e', &format!("{prompt_text}: "))?;View on GitHub (pinned to 82976d349a)