FuelLabs/sway · error

failed to read {}: {}

Error message

failed to read {}: {}

What it means

Lock::from_path loads Forc.lock with fs::read_to_string; failure at that I/O stage is wrapped with the offending path. The file was not read at all - missing, permission denied, path is a directory, or non-UTF-8 bytes. It is deliberately distinct from the 'failed to parse lock file' TOML error raised on the next line for content problems.

Source

Thrown at forc-pkg/src/lock.rs:157

    pub fn name_disambiguated(&self, disambiguate: &HashSet<&str>) -> Cow<str> {
        let disambiguate = disambiguate.contains(&self.name[..]);
        pkg_name_disambiguated(&self.name, &self.source, disambiguate)
    }
}

/// Represents a `DepKind` before getting parsed.
///
/// Used to carry on the type of the `DepKind` until parsing. After parsing pkg_dep_line converted into `DepKind`.
enum UnparsedDepKind {
    Library,
    Contract,
}

impl Lock {
    /// Load the `Lock` structure from the TOML `Forc.lock` file at the specified path.
    pub fn from_path(path: &Path) -> Result<Self> {
        let string = fs::read_to_string(path)
            .map_err(|e| anyhow!("failed to read {}: {}", path.display(), e))?;
        toml::de::from_str(&string).map_err(|e| anyhow!("failed to parse lock file: {}", e))
    }

    /// Given a graph of pinned packages, create a `Lock` representing the `Forc.lock` file
    /// structure.
    pub fn from_graph(graph: &pkg::Graph) -> Self {
        let names = graph.node_indices().map(|n| &graph[n].name[..]);
        let disambiguate: HashSet<_> = names_requiring_disambiguation(names).collect();
        // Collect the packages.
        let package: BTreeSet<_> = graph
            .node_indices()
            .map(|node| PkgLock::from_node(graph, node, &disambiguate))
            .collect();
        Self { package }
    }

    /// Given a `Lock` loaded from a `Forc.lock` file, produce the graph of pinned dependencies.
    pub fn to_graph(&self) -> Result<pkg::Graph> {

View on GitHub (pinned to 47e5e902fa)

Solutions

  1. Verify the path: Forc.lock must exist next to Forc.toml in the project directory you are operating on.
  2. Run forc build once to generate the lock file before invoking APIs that load it.
  3. Fix permissions or remount read-only filesystems so the file is readable by the current user.
  4. If the file is corrupted or non-UTF-8, delete it and re-resolve dependencies with a fresh build.
Defensive patterns

Strategy: validation

Validate before calling

// Rust, before calling Lock::from_path:
use std::fs;
fn lock_readable(p: &std::path::Path) -> bool {
    match fs::metadata(p) {
        Ok(m) => m.is_file() && fs::read(p).map(|b| String::from_utf8(b).is_ok()).unwrap_or(false),
        Err(_) => false,
    }
}
if !lock_readable(&lock_path) { /* generate lock via forc build instead of failing */ }

Try / catch

// Lock::from_path returns anyhow::Result - match and special-case I/O kinds:
match Lock::from_path(&path) {
    Ok(lock) => { /* ... */ }
    Err(e) if e.to_string().starts_with("failed to read") => {
        // treat as 'lock missing/unreadable': regenerate rather than abort
    }
    Err(e) => { /* parse error - regenerate or surface */ }
}

Prevention

When it happens

Trigger: Calling Lock::from_path (directly or via forc-pkg flows such as BuildPlan::from_lock_and_manifests) where no Forc.lock exists yet, is unreadable, or contains non-UTF-8 bytes.

Common situations: First build in a fresh clone before lock generation; deleted or moved lock file; restrictive permissions or read-only mounts in CI; a binary/corrupted lock file; concurrent forc processes rewriting Forc.lock mid-read.

Related errors


AI-assisted analysis of FuelLabs/sway@47e5e902fa (2026-08-16). Data as JSON: /api/errors/fa096f303b6b8b82. Report an issue: GitHub.