janhq/jan · error · ServerError
IO error
Error message
IO error: {0} What it means
ServerError::Io wraps std::io::Error from command handlers in the plugin, displayed as "IO error: {0}". It is produced automatically via #[from] whenever a server command performs file or process I/O that fails. It lets the ServerError enum cover filesystem and stdio failures without a bespoke variant.
Solutions
- Inspect the wrapped std::io::Error and its kind; check NotFound, PermissionDenied, etc.
- Verify the configured model/binary paths exist and are readable/executable before invoking the command.
- Check disk space and file permissions in the plugin's data directories.
- If path-dependent, resolve with an absolute path and validate it on the frontend first.
Example fix
// before: passing a relative, unverified model path
let path = "models/foo.gguf";
// after: validate before calling the command
let path = std::path::absolute("models/foo.gguf")?;
if !path.exists() { return Err(ServerError::InvalidArgument(format!("missing model {}", path.display()))); } Defensive patterns
Strategy: validation
Validate before calling
fn ensure_readable(path: &std::path::Path) -> Result<(), ServerError> {
let meta = std::fs::metadata(path).map_err(ServerError::Io)?;
if meta.is_file() { Ok(()) } else { Err(ServerError::InvalidArgument(format!("{} is not a file", path.display()))) }
} Try / catch
match cmd() {
Err(ServerError::Io(e)) => match e.kind() {
std::io::ErrorKind::NotFound => show("path not found"),
std::io::ErrorKind::PermissionDenied => show("check permissions"),
_ => show(&e.to_string()),
},
other => other,
} Prevention
- Validate model/binary paths exist and are accessible before invoking commands.
- Use absolute paths resolved at startup.
- Check disk space for cache/write-heavy operations.
- Verify the llama-server binary is present and executable after install.
When it happens
Trigger: A #[tauri::command] returning ServerError performs std::io operations (reading model files, spawning the llama server process, writing temp files) and the underlying call returns Err(std::io::Error), converted by the #[from] impl.
Common situations: Model file missing or unreadable at the configured path, disk full when writing caches, permission denied on the llama-server binary, or process spawn failures (executable not found).
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17).
Data as JSON: /api/errors/4ba5def18fb95e72.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/plugins/tauri-plugin-llamacpp/src/error.rs:199
fn basename(path: &str) -> &str {
path.rsplit(['/', '\\']).next().unwrap_or(path)
}
fn push_library(found: &mut Vec<String>, candidate: &str) {
let candidate = candidate.trim_matches(['\'', '"', '(', ')', ',', '.'].as_ref());
if looks_like_library(candidate) && !found.iter().any(|f| f == candidate) {
found.push(candidate.to_string());
}
}
// Error type for server commands
#[derive(Debug, thiserror::Error)]
pub enum ServerError {
#[error(transparent)]
Llamacpp(#[from] LlamacppError),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Tauri error: {0}")]
Tauri(#[from] tauri::Error),
#[error("Invalid argument: {0}")]
InvalidArgument(String),
}
// impl serialization for tauri
impl serde::Serialize for ServerError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let error_to_serialize: LlamacppError = match self {
ServerError::Llamacpp(err) => err.clone(),
ServerError::Io(e) => LlamacppError::new(View on GitHub (pinned to 7205d770c1)