nikivdev/code · warning

Non-interactive session; cannot confirm action.

Error message

Non-interactive session; cannot confirm action.

What it means

prompt_yes_no refuses to prompt when stdin is not a TTY: in non-interactive sessions (CI, piped input, scripts) there is no human to confirm, so it bails rather than guessing. This is reached when an import needs confirmation, e.g. a dirty source workspace.

Source

Thrown at src/ext.rs:415

fn git_capture_in(repo_root: &Path, args: &[&str]) -> Result<String> {
    let output = Command::new("git")
        .current_dir(repo_root)
        .args(args)
        .output()
        .with_context(|| format!("failed to run git {}", args.join(" ")))?;
    if !output.status.success() {
        bail!("git {} failed", args.join(" "));
    }
    Ok(String::from_utf8_lossy(&output.stdout).to_string())
}

fn prompt_yes_no(message: &str, default_yes: bool) -> Result<bool> {
    let prompt = if default_yes { "[Y/n]" } else { "[y/N]" };
    print!("{message} {prompt}: ");
    io::stdout().flush()?;
    if !io::stdin().is_terminal() {
        bail!("Non-interactive session; cannot confirm action.");
    }
    let mut input = String::new();
    io::stdin().read_line(&mut input)?;
    let answer = input.trim().to_ascii_lowercase();
    if answer.is_empty() {
        return Ok(default_yes);
    }
    Ok(answer == "y" || answer == "yes")
}

View on GitHub (pinned to a747e741ae)

Solutions

  1. Make the source workspace clean (commit with `jj commit` or `jj new`) so no confirmation prompt is needed.
  2. Run the command in an interactive terminal and answer the prompt.
  3. If automation must proceed, arrange for an explicit non-prompting code path or pre-clean state in the pipeline.

Example fix

// before (CI step)
run(["import-external", "~/repos/ws"]); // dirty ws -> needs prompt -> fails
// after (CI step: clean first)
run(["jj", "-R", "~/repos/ws", "commit", "-m", "wip"]);
run(["import-external", "~/repos/ws"]);
Defensive patterns

Strategy: validation

Validate before calling

use std::io::IsTerminal;
if !std::io::stdin().is_terminal() {
    return Err(anyhow!("Non-interactive session: pre-commit changes in the source so no prompt is needed"));
}
import_external_path(source)?;

Try / catch

if let Err(e) = import_external_path(source) {
    if e.to_string().contains("Non-interactive session") {
        eprintln!("Run interactively, or clean the source workspace first (jj commit)");
    } else { return Err(e.into()); }
}

Prevention

When it happens

Trigger: import_external_path on a workspace with uncommitted changes while running under CI, a non-interactive shell, or with stdin redirected from a file/pipe, so io::stdin().is_terminal() is false.

Common situations: Automation pipelines invoking the import command; running through ssh without a TTY; piping output (e.g. `tool import ... | tee log`) which does not affect stdin but CI runners often have no TTY at all.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/1ab4f14217d5964e. Report an issue: GitHub.