sigoden/aichat · error

Failed to init rag in non-interactive mode

Error message

Failed to init rag in non-interactive mode

What it means

`Rag::init` builds a RAG index interactively (asking the user to pick embedding/reranker models and chunk parameters). In non-interactive mode there is no way to collect these choices, so it bails with 'Failed to init rag in non-interactive mode'.

Solutions

  1. Run the RAG init in an interactive terminal once; reuse the saved index non-interactively afterwards
  2. Pre-create the RAG config file at save_path so init prompts are skipped
  3. Patch/wrap so defaults are supplied programmatically instead of prompts

Example fix

// before (CI script)
mytool --rag init docs/
// after: init locally, then reuse
mytool init-rag docs/   # in a TTY
ci_job: mytool --rag docs/ query "..."
Defensive patterns

Strategy: try-catch

Validate before calling

if !std::io::IsTerminal::is_terminal(&std::io::stdout()) {
    eprintln!("RAG init requires a TTY; run it interactively first");
}

Type guard

fn can_init_rag() -> bool {
    std::io::IsTerminal::is_terminal(&std::io::stdout())
}

Try / catch

match Rag::init(config, name, &save_path, &docs, signal).await {
    Err(e) if e.to_string().contains("non-interactive mode") => {
        eprintln!("Initialize RAG in a TTY, then reuse the saved index");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `Rag::init` (with a name, save path, and doc paths) when `IS_STDOUT_TERMINAL` is false — e.g. piped stdout, CI, scripts.

Common situations: Initializing a RAG document store from a script or Docker container without a TTY; automation pipelines invoking RAG setup.

Related errors


AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09). Data as JSON: /api/errors/fc411bfbf9530e3d. Report an issue: GitHub.

Appendix: source

Thrown at src/rag/mod.rs:67

            embedding_model: self.embedding_model.clone(),
            hnsw: self.data.build_hnsw(),
            bm25: self.data.build_bm25(),
            data: self.data.clone(),
            last_sources: RwLock::new(None),
        }
    }
}

impl Rag {
    pub async fn init(
        config: &GlobalConfig,
        name: &str,
        save_path: &Path,
        doc_paths: &[String],
        abort_signal: AbortSignal,
    ) -> Result<Self> {
        if !*IS_STDOUT_TERMINAL {
            bail!("Failed to init rag in non-interactive mode");
        }
        println!("⚙ Initializing RAG...");
        let (embedding_model, chunk_size, chunk_overlap) = Self::create_config(config)?;
        let (reranker_model, top_k) = {
            let config = config.read();
            (config.rag_reranker_model.clone(), config.rag_top_k)
        };
        let data = RagData::new(
            embedding_model.id(),
            chunk_size,
            chunk_overlap,
            reranker_model,
            top_k,
            embedding_model.max_batch_size(),
        );
        let mut rag = Self::create(config, name, save_path, data)?;
        let mut paths = doc_paths.to_vec();
        if paths.is_empty() {

View on GitHub (pinned to 82976d349a)