{"record":{"id":"1ab4f14217d5964e","repo":"nikivdev/code","slug":"non-interactive-session-cannot-confirm-action","errorCode":null,"errorMessage":"Non-interactive session; cannot confirm action.","messagePattern":"Non-interactive session; cannot confirm action\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/ext.rs","lineNumber":415,"sourceCode":"\nfn git_capture_in(repo_root: &Path, args: &[&str]) -> Result<String> {\n    let output = Command::new(\"git\")\n        .current_dir(repo_root)\n        .args(args)\n        .output()\n        .with_context(|| format!(\"failed to run git {}\", args.join(\" \")))?;\n    if !output.status.success() {\n        bail!(\"git {} failed\", args.join(\" \"));\n    }\n    Ok(String::from_utf8_lossy(&output.stdout).to_string())\n}\n\nfn prompt_yes_no(message: &str, default_yes: bool) -> Result<bool> {\n    let prompt = if default_yes { \"[Y/n]\" } else { \"[y/N]\" };\n    print!(\"{message} {prompt}: \");\n    io::stdout().flush()?;\n    if !io::stdin().is_terminal() {\n        bail!(\"Non-interactive session; cannot confirm action.\");\n    }\n    let mut input = String::new();\n    io::stdin().read_line(&mut input)?;\n    let answer = input.trim().to_ascii_lowercase();\n    if answer.is_empty() {\n        return Ok(default_yes);\n    }\n    Ok(answer == \"y\" || answer == \"yes\")\n}\n","sourceCodeStart":397,"sourceCodeEnd":425,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/ext.rs#L397-L425","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (CI step)\nrun([\"import-external\", \"~/repos/ws\"]); // dirty ws -> needs prompt -> fails\n// after (CI step: clean first)\nrun([\"jj\", \"-R\", \"~/repos/ws\", \"commit\", \"-m\", \"wip\"]);\nrun([\"import-external\", \"~/repos/ws\"]);","handlingStrategy":"validation","validationCode":"use std::io::IsTerminal;\nif !std::io::stdin().is_terminal() {\n    return Err(anyhow!(\"Non-interactive session: pre-commit changes in the source so no prompt is needed\"));\n}\nimport_external_path(source)?;","typeGuard":null,"tryCatchPattern":"if let Err(e) = import_external_path(source) {\n    if e.to_string().contains(\"Non-interactive session\") {\n        eprintln!(\"Run interactively, or clean the source workspace first (jj commit)\");\n    } else { return Err(e.into()); }\n}","preventionTips":["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."],"tags":["io","interactive-terminal","ci"],"backgroundTag":"non-interactive-terminal","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}