janhq/jan · error · RagError::IoError

IO error: {0}

Error message

IO error: {0}

What it means

`RagError::IoError(String)` wraps a `std::io::Error` via the `From` impl — the wrapped string is `err.to_string()`. It fires when the RAG plugin itself hits an I/O failure while reading a source file, writing extracted text to a cache, or manipulating the vector store's files.

Source

Thrown at src-tauri/plugins/tauri-plugin-rag/src/error.rs:11

use serde::{Deserialize, Serialize};

#[derive(Debug, thiserror::Error, Serialize, Deserialize)]
pub enum RagError {
    #[error("Failed to parse document: {0}")]
    ParseError(String),

    #[error("Unsupported file type: {0}")]
    UnsupportedFileType(String),

    #[error("IO error: {0}")]
    IoError(String),
}

impl From<std::io::Error> for RagError {
    fn from(err: std::io::Error) -> Self {
        RagError::IoError(err.to_string())
    }
}

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Inspect the wrapped message for the io::Error kind (NotFound, PermissionDenied, UnexpectedEof, etc.).
  2. Verify the path is absolute and exists before calling ingest.
  3. For Tauri apps, ensure the path is within scoped access (asset protocol / fs scope).
  4. For permission errors, guide the user to grant the necessary filesystem access.

Example fix

// before - From impl flattens the kind into a string
impl From<std::io::Error> for RagError {
    fn from(err: std::io::Error) -> Self { RagError::IoError(err.to_string()) }
}

// after - preserve kind for caller branching
pub enum RagError {
    IoError { kind: io::ErrorKind, message: String },
    // ...
}
impl From<std::io::Error> for RagError {
    fn from(err: std::io::Error) -> Self {
        RagError::IoError { kind: err.kind(), message: err.to_string() }
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

fn readable(path: &Path) -> bool {
    std::fs::File::open(path).is_ok()
}

Type guard

null

Try / catch

match rag.ingest(&path).await {
    Ok(doc) => Ok(doc),
    Err(RagError::IoError(msg)) if msg.contains("os error 2") => {
        Err(UserError::NotFound(path.display().to_string()))
    }
    Err(RagError::IoError(msg)) if msg.contains("os error 13") => {
        Err(UserError::PermissionDenied(path.display().to_string()))
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Opening a file that does not exist or is unreadable; permission denied on the ingestion directory; disk full while writing the index; broken pipe or unexpected EOF mid-read on a truncated file.

Common situations: Wrong path passed by the user; permission issues on macOS due to App Sandbox / TCC; concurrent write contention on the index file; a file that was deleted between the UI listing it and the backend opening it.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/38335e1a03ca8c17. Report an issue: GitHub.