rust-lang/cargo · error

unable to read .cargo-ok file at

Error message

unable to read .cargo-ok file at {path:?}: {e}

What it means

When unpacking a `.crate` tarball, Cargo reads the marker `.cargo-ok` file inside the unpack directory to detect a previously-completed unpack. If reading that file fails with an I/O error other than `NotFound` (e.g. permission denied, disk error), Cargo bails rather than guessing. `NotFound` is tolerated (first unpack).

Solutions

  1. Fix permissions: `chown -R $USER ~/.cargo` and `chmod -R u+rwX ~/.cargo/registry/src`.
  2. Remove the corrupt unpack directory: `rm -rf ~/.cargo/registry/src/<index>/<pkg>-<ver>` then rebuild.
  3. Disable/reconfigure antivirus or SELinux that may block reads under `~/.cargo`.
  4. Free disk space / remount read-write if the filesystem is read-only.

Example fix

# before: permission-denied / corrupt .cargo-ok
$ cargo build
error: unable to read .cargo-ok file at ".../.cargo-ok": Permission denied

# after
$ chmod -R u+rwX ~/.cargo/registry/src
$ rm -rf ~/.cargo/registry/src/<index>/<pkg>-<ver>
$ cargo build
Defensive patterns

Strategy: try-catch

Validate before calling

fn can_read_ok_file(path: &Path) -> bool {
    match std::fs::read_to_string(path) {
    Ok(_) => true,
    Err(e) if e.kind() == std::io::ErrorKind::NotFound => true,
    Err(_) => false,
    }
}

Type guard

fn ok_file_accessible(path: &std::path::Path) -> bool {
    std::fs::metadata(path).map_or(true, |_| std::fs::read_to_string(path).is_ok())
}

Try / catch

match fs::read_to_string(&path) {
    Ok(s) => /* parse */,
    Err(e) if e.kind() == io::ErrorKind::NotFound => {},
    Err(e) => {
        // attempt recovery: fix perms or wipe the unpack dir, then retry once
        let _ = fs::remove_dir_all(dst.as_path_unlocked());
        return unpack(...);
    }
}

Prevention

When it happens

Trigger: In the unpack flow, `fs::read_to_string(<dst>/.cargo-ok)` returns an I/O error whose `kind()` is not `NotFound`. Causes: permission denied, I/O error, broken symlink, or filesystem corruption at the unpack path.

Common situations: Permissions on `~/.cargo/registry/src` wrong (read-only or owned by another user); a broken `.cargo-ok` symlink left by a crashed unpack; antivirus/SELinux blocking reads; full or read-only filesystem; NFS hiccup.

Related errors


AI-assisted analysis of rust-lang/cargo@98a09e7e7d (2026-08-11). Data as JSON: /api/errors/ea6580d267e3b020. Report an issue: GitHub.

Appendix: source

Thrown at src/sources/registry/mod.rs:586

                        .mark_registry_src_used(global_cache_tracker::RegistrySrc {
                            encoded_registry_name: self.name,
                            package_dir: package_dir.into(),
                            size: None,
                        });
                    return Ok(unpack_dir.to_path_buf());
                }
                _ => {
                    if ok == "ok" {
                        tracing::debug!("old `ok` content found, clearing cache");
                    } else {
                        tracing::warn!("unrecognized .cargo-ok content, clearing cache: {ok}");
                    }
                    // See comment of `unpack_package` about why removing all stuff.
                    paths::remove_dir_all(dst.as_path_unlocked())?;
                }
            },
            Err(e) if e.kind() == io::ErrorKind::NotFound => {}
            Err(e) => anyhow::bail!("unable to read .cargo-ok file at {path:?}: {e}"),
        }
        dst.create_dir()?;

        let bytes_written = unpack(self.gctx, tarball, unpack_dir, &|_| true)?;
        update_mtime_for_generated_files(unpack_dir);

        // Now that we've finished unpacking, create and write to the lock file to indicate that
        // unpacking was successful.
        let mut ok = OpenOptions::new()
            .create_new(true)
            .read(true)
            .write(true)
            .open(&path)
            .with_context(|| format!("failed to open `{}`", path.display()))?;

        let lock_meta = LockMetadata { v: 1 };
        write!(ok, "{}", serde_json::to_string(&lock_meta).unwrap())?;

View on GitHub (pinned to 98a09e7e7d)