aaif-goose/goose · error

Tool approval required in non-interactive mode with GooseMod

Error message

Tool approval required in non-interactive mode with GooseMode::{goose_mode}. This is an invalid configuration — Approve/SmartApprove modes require an interactive terminal. Use GooseMode::Auto for headless sessions.

What it means

When a tool needs confirmation and the session is not interactive (piped stdin/stdout, headless run), goose refuses to auto-approve while GooseMode is Approve or SmartApprove, because silently allowing would break the safety contract those modes promise. The stream is cancelled, the token dropped, and this error returned; {goose_mode} interpolates the offending mode name.

Source

Thrown at crates/goose-cli/src/session/mod.rs:1489

                result = stream.next() => {
                    match result {
                        Some(Ok(AgentEvent::Message(message))) => {
                            if first_token_at.is_none() && message_has_text(&message) {
                                first_token_at = Some(Instant::now());
                            }
                            if let Some((id, security_prompt)) = find_tool_confirmation(&message) {
                                let permission = if interactive {
                                    prompt_tool_confirmation(&security_prompt)?
                                } else {
                                    // Non-interactive/headless mode: refuse to run in
                                    // Approve/SmartApprove modes since auto-allowing would
                                    // bypass the safety contract those modes are meant to enforce.
                                    let config = Config::global();
                                    let goose_mode = config.get_goose_mode().unwrap_or(GooseMode::Auto);
                                    if goose_mode == GooseMode::Approve || goose_mode == GooseMode::SmartApprove {
                                        cancel_token_clone.cancel();
                                        drop(stream);
                                        return Err(anyhow::anyhow!(
                                            "Tool approval required in non-interactive mode with GooseMode::{goose_mode}. \
                                             This is an invalid configuration — Approve/SmartApprove modes require an \
                                             interactive terminal. Use GooseMode::Auto for headless sessions."
                                        ));
                                    }
                                    tracing::warn!(
                                        "Tool confirmation required in non-interactive mode, auto-allowing"
                                    );
                                    Permission::AllowOnce
                                };

                                if permission == Permission::Cancel {
                                    output::render_text("Tool call cancelled. Returning to chat...", Some(Color::Yellow), true);
                                    self.agent.handle_confirmation(id.clone(), PermissionConfirmation {
                                        principal_type: PrincipalType::Tool,
                                        permission: Permission::DenyOnce,
                                    }).await;
                                    let mut response_message = Message::user();

View on GitHub (pinned to 3810898a74)

Solutions

  1. Set GOOSE_MODE=auto (or remove the mode override) for headless runs
  2. Run the session in an interactive terminal when Approve/SmartApprove is intended
  3. Disable approval-requiring extensions in headless profiles so nothing requests confirmation

Example fix

# before
GOOSE_MODE=approve goose run -i 'clean the repo'
# after
GOOSE_MODE=auto goose run -i 'clean the repo'
Defensive patterns

Strategy: validation

Validate before calling

let mode = std::env::var("GOOSE_MODE").unwrap_or_else(|_| "auto".into());
if !interactive && (mode == "approve" || mode == "smart_approve") {
    panic!("headless sessions cannot use {} mode; set GOOSE_MODE=auto", mode);
}

Try / catch

match run_headless().await {
    Err(e) if e.to_string().contains("non-interactive mode") => {
        eprintln!("rerun with GOOSE_MODE=auto or an interactive terminal");
        std::process::exit(2);
    }
    other => other,
}

Prevention

When it happens

Trigger: goose execute / goose run / piped-stdin sessions with GOOSE_MODE=approve (or smart_approve persisted in config) while any enabled extension requests tool confirmation during the reply loop.

Common situations: CI or cron scripts reusing an interactive-era config; GOOSE_MODE left exported in the shell; scheduled jobs expecting Auto behavior.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/6379b487fb70f452. Report an issue: GitHub.