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
- Run the RAG init in an interactive terminal once; reuse the saved index non-interactively afterwards
- Pre-create the RAG config file at save_path so init prompts are skipped
- 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
- Run RAG init once interactively; reuse the saved index in automation
- Pre-create the RAG config at save_path with model/chunk settings
- Avoid invoking RAG init from CI or piped contexts
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)