xai-org/grok-build · error

FS_OTHER

FS_OTHER

Error message

An unexpected I/O error occurred.

What it means

A persistence I/O error did not match any classified io::ErrorKind (NotFound, PermissionDenied, StorageFull, etc.), so it falls into the catch-all arm. The library converts it into an ACP protocol error with code FS_OTHER and message 'An unexpected I/O error occurred.', while logging the raw error, kind, and raw OS error code and attaching the full detail in error data.

Source

Thrown at crates/codegen/xai-grok-shell/src/session/persistence.rs:2511

    false
}

/// Map a persistence `io::Error` into an `acp::Error` with a human-friendly
/// `message` and a stable `data.code` for log aggregation.
pub(crate) fn io_error_to_acp(e: &io::Error) -> acp::Error {
    let (message, code) = if is_disk_full_io_error(e) {
        ("No space left on device", "FS_DISK_QUOTA_EXCEEDED")
    } else {
        match e.kind() {
            io::ErrorKind::NotFound => ("Path not found.", "FS_NOT_FOUND"),
            io::ErrorKind::PermissionDenied => ("Permission denied.", "FS_PERMISSION_DENIED"),
            _ => {
                tracing::warn!(error = %e, kind = ?e.kind(), raw_os = ?e.raw_os_error(), "unclassified persistence I/O error");
                ("An unexpected I/O error occurred.", "FS_OTHER")
            }
        }
    };
    acp::Error::new(acp::ErrorCode::InternalError.into(), message.to_string()).data(Some(
        serde_json::json!({
            "code": code,
            "detail": e.to_string(),
        }),
    ))
}

#[cfg(test)]
#[path = "persistence_io_error_to_acp_tests.rs"]
mod io_error_to_acp_tests;

/// Best-effort worktree liveness touch: stamp `last_accessed_at` on the
/// worktree containing this session's cwd so `grok worktree gc` expires by
/// last use, not creation time. Lives here — not in a `StorageAdapter` —
/// so every session create/load path shares it regardless of backend.
fn spawn_worktree_touch(info: &Info) -> tokio::task::JoinHandle<()> {
    let cwd = info.cwd.clone();
    tokio::task::spawn_blocking(move || {

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Read error.data.detail and the tracing log (kind + raw_os_error) to identify the underlying OS error
  2. Look up the raw OS errno (e.g. via `errno` docs) to find the real cause
  3. Check filesystem health/mount (dmesg, df, mount options) for the session directory volume
  4. Move the session directory to a local filesystem if a network mount keeps producing uncategorized errors
Defensive patterns

Strategy: try-catch

Try / catch

// ACP client side: inspect code + data.detail
if err.code == acp::ErrorCode::InternalError && err.data["code"] == "FS_OTHER" {
    let detail = err.data["detail"].as_str().unwrap_or("");
    tracing::error!(detail, "unclassified persistence I/O error; check raw_os_error in server logs");
    // decide retry vs fail based on detail (e.g. EINTR/EAGAIN → retry, hardware → fail)
}

Prevention

When it happens

Trigger: Any append/persistence I/O operation returning an io::Error whose kind is outside the recognized set — e.g. ErrorKind::FilesystemLoop, IsADirectory, Uncategorized, or unusual errno from exotic filesystems — flowing into the error-to-ACP mapping.

Common situations: Session directory on NFS/network mounts returning unusual errnos; antivirus/backup tools locking files; odd permission setups (immutable files, selinux denials surfacing as uncategorized errors); disk quotas surfacing with unexpected kinds.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/ec4e3a7122b51175. Report an issue: GitHub.