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

  1. Rephrase the prompt so the model produces an actual command
  2. Retry the request (transient empty completions)
  3. Check the provider response — empty completions may indicate an API/model issue
  4. 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

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


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)