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
- Make the source workspace clean (commit with `jj commit` or `jj new`) so no confirmation prompt is needed.
- Run the command in an interactive terminal and answer the prompt.
- 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
- Pre-clean the source workspace in CI so the confirmation path is never reached.
- Allocate a TTY in CI steps (e.g. `script -qc` or ssh -t) if prompts are intended.
- Avoid piping stdin when running commands that may prompt.
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
- {} {} requires an interactive terminal (TTY); run this in a
- Invariant violations found (mode=block): {} finding(s)
- Error reading log file: {}
- failed to read from codex app-server: {}
- failed to persist {}: {}
AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01).
Data as JSON: /api/errors/1ab4f14217d5964e.
Report an issue: GitHub.