janhq/jan · error · ServerError::Io
IO error: {0}
Error message
IO error: {0} What it means
`ServerError::Io` wraps any `std::io::Error` propagated out of the MLX plugin (via `#[from]`). It fires when the plugin itself hits an I/O problem — not the MLX subprocess: e.g. the model file cannot be opened, a temp file cannot be written, or a pipe to the subprocess cannot be created. The custom `Serialize` impl converts this variant into an `MlxError` with `ErrorCode::IoError` before serializing.
Source
Thrown at src-tauri/plugins/tauri-plugin-mlx/src/error.rs:62
"Out of memory. The model requires more RAM than available.".into(),
Some(stderr.into()),
);
}
Self::new(
ErrorCode::MlxProcessError,
"The MLX model process encountered an unexpected error.".into(),
Some(stderr.into()),
)
}
}
#[derive(Debug, thiserror::Error)]
pub enum ServerError {
#[error(transparent)]
Mlx(#[from] MlxError),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Tauri error: {0}")]
Tauri(#[from] tauri::Error),
}
impl serde::Serialize for ServerError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let error_to_serialize: MlxError = match self {
ServerError::Mlx(err) => err.clone(),
ServerError::Io(e) => MlxError::new(
ErrorCode::IoError,
"An input/output error occurred.".into(),
Some(e.to_string()),
),View on GitHub (pinned to fad3f12a14)
Solutions
- Inspect the wrapped `io::Error` — its `kind()` tells you NotFound, PermissionDenied, BrokenPipe, etc.
- For NotFound, verify the path the plugin resolved (log it before opening).
- For PermissionDenied, check filesystem permissions on the model and cache directories.
- For BrokenPipe, handle the case where the MLX process exits mid-write (often a downstream crash — see the MlxProcessError).
Example fix
// before
let f = File::open(&path)?;
// after - map io::Error to a richer MlxError with context
let f = File::open(&path).map_err(|e| match e.kind() {
io::ErrorKind::NotFound => MlxError::new(ErrorCode::ModelFileNotFound, format!("{} not found", path.display()), None),
_ => MlxError::new(ErrorCode::IoError, e.to_string(), Some(format!("{}", path.display()))),
})?; Defensive patterns
Strategy: try-catch
Validate before calling
fn ensure_readable(path: &Path) -> io::Result<()> {
std::fs::File::open(path).map(|_| ())
} Type guard
null
Try / catch
match run_mlx(&req).await {
Ok(out) => Ok(out),
Err(ServerError::Io(e)) if e.kind() == io::ErrorKind::NotFound => {
Err(UserError::NotFound(req.model.clone()))
}
Err(ServerError::Io(e)) if e.kind() == io::ErrorKind::PermissionDenied => {
Err(UserError::PermissionDenied(req.model.clone()))
}
Err(e) => Err(e.into()),
} Prevention
- Check the model path exists and is readable before launching MLX.
- Inspect `io::Error::kind()` to give users actionable messages.
- Handle BrokenPipe as a likely subprocess crash and re-check the process state.
When it happens
Trigger: Opening a model file that does not exist or is not readable; creating a named pipe / socket to talk to MLX and failing; writing cache or log files to a directory without permission; the subprocess stdin/stdout pipe returning EPIPE.
Common situations: Wrong model path; permission errors on the model directory or cache directory; disk full; broken pipe when the MLX process exits while the plugin is still writing to its stdin.
Related errors
- IO error: {0}
- IO error: {0}
- MlxError {{ code: {code:?}, message: "{message}" }}
- Failed to decompress archive: ${String(e)}
- ${String(e)}
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/11398c2cd9cccd47.
Report an issue: GitHub.