janhq/jan · error · ServerError

IO error

Error message

IO error: {0}

What it means

ServerError::Io wraps a std::io::Error raised inside the tauri-plugin-mlx server and displays as "IO error: {0}". It is produced via `#[from]` whenever an IO operation (file read/write, process spawn, socket) fails while managing the MLX server. It is a transparent wrapper over the OS-level error.

Solutions

  1. Inspect the inner io::Error (source/kind) for the precise OS cause (NotFound, PermissionDenied, etc.).
  2. Fix the referenced path: confirm the file/binary exists and has correct permissions.
  3. Check disk space and directory writability for logs/temp files.
  4. If it's a socket error, free the occupied port or choose another one.

Example fix

// before
let server = Server::start(config).unwrap();

// after
let server = Server::start(config).map_err(|e| match &e {
    ServerError::Io(io) if io.kind() == std::io::ErrorKind::NotFound => {
        anyhow!("server binary or model path missing: {}", io)
    }
    other => anyhow!("server start failed: {other}"),
})?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: preflight path checks before starting the server
fn path_ok(p: &Path) -> bool {
    p.exists() && std::fs::metadata(p).map(|m| !m.permissions().readonly()).unwrap_or(false)
}

Type guard

fn as_io_error(err: &ServerError) -> Option<&std::io::Error> {
    match err { ServerError::Io(e) => Some(e), _ => None }
}

Try / catch

match Server::start(cfg) {
    Err(ServerError::Io(io)) => match io.kind() {
        std::io::ErrorKind::NotFound => bail!("missing file: {io}"),
        std::io::ErrorKind::PermissionDenied => bail!("fix permissions: {io}"),
        _ => bail!("io failure: {io}"),
    },
    other => other?,
}

Prevention

When it happens

Trigger: Starting/stopping the MLX server when a log or model file can't be opened, the server binary can't be spawned, or a port/socket operation fails with an OS error.

Common situations: Wrong model or working-directory paths; missing execute permissions on the server binary; disk-full or permission-denied conditions; port already bound surfacing as an OS error.

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/11398c2cd9cccd47. Report an issue: GitHub.

Appendix: 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 7205d770c1)