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
- Verify the path: Forc.lock must exist next to Forc.toml in the project directory you are operating on.
- Run forc build once to generate the lock file before invoking APIs that load it.
- Fix permissions or remount read-only filesystems so the file is readable by the current user.
- 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
- Run one forc build in fresh clones so Forc.lock exists before tooling reads it.
- Do not delete or relocate Forc.lock independently of Forc.toml.
- Keep lock files UTF-8 text and readable in CI images.
- Treat lock read failures as a signal to re-resolve, not to hand-craft the file.
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
- invalid 'source' entry for package {} lock: {:?}
- failed to parse dependency "{}": {}
- found dep {} without node entry in graph
- missing closing parenthesis
- missing pkg string
AI-assisted analysis of FuelLabs/sway@47e5e902fa (2026-08-16).
Data as JSON: /api/errors/fa096f303b6b8b82.
Report an issue: GitHub.