FuelLabs/fuels-rs · error · std::io::Error

failed to read storage slots from: {storage_path:?}: {e}

Error message

failed to read storage slots from: {storage_path:?}: {e}

What it means

Runtime IO error from StorageSlots::load_from_file in fuels-programs: the storage-slots JSON passed existence and .json-extension checks but std::fs::read_to_string failed. The io::ErrorKind is preserved and the path plus underlying cause are embedded. This loader also runs implicitly via StorageConfiguration autoloading, which looks for <binary_stem>-storage_slots.json next to the contract binary.

Source

Thrown at packages/fuels-programs/src/contract/storage.rs:101

            storage_slots: pairs.collect(),
        }
    }

    pub(crate) fn add_overrides(
        &mut self,
        storage_slots: impl IntoIterator<Item = StorageSlot>,
    ) -> &mut Self {
        let pairs = storage_slots.into_iter().map(|slot| (*slot.key(), slot));
        self.storage_slots.extend(pairs);
        self
    }

    pub(crate) fn load_from_file(storage_path: impl AsRef<Path>) -> Result<Self> {
        let storage_path = storage_path.as_ref();
        validate_path_and_extension(storage_path, "json")?;

        let storage_json_string = std::fs::read_to_string(storage_path).map_err(|e| {
            io::Error::new(
                e.kind(),
                format!("failed to read storage slots from: {storage_path:?}: {e}"),
            )
        })?;

        let decoded_slots = serde_json::from_str::<Vec<StorageSlot>>(&storage_json_string)?;

        Ok(StorageSlots::from(decoded_slots))
    }

    pub(crate) fn into_iter(self) -> impl Iterator<Item = StorageSlot> {
        self.storage_slots.into_values()
    }
}

pub(crate) fn determine_storage_slots(
    storage_config: StorageConfiguration,
    binary_filepath: &Path,

View on GitHub (pinned to d9a250a518)

Solutions

  1. Read the embedded {e} cause and fix at OS level: chmod/copy with correct ownership for 'Permission denied'; re-run forc build if the file was being written concurrently.
  2. If the JSON is intentionally not wanted and autoloading keeps hitting it, disable autoloading via StorageConfiguration (set autoload to false) so the sibling file is ignored.
  3. Validate the JSON parses after access is fixed — the next failure mode is serde_json::Error on malformed content.
  4. Point storage to an explicit, known-good file path in LoadConfiguration/StorageConfiguration instead of relying on the sibling-file convention.

Example fix

// before: autoloading picks up an unreadable sibling JSON and fails
let config = LoadConfiguration::default();
// after: disable autoloading so the sibling file is ignored
let config = LoadConfiguration::new().with_storage_configuration(StorageConfiguration::default().with_autoload(false));
Defensive patterns

Strategy: try-catch

Validate before calling

let storage_path = std::path::Path::new(&storage_file);
if storage_path.exists() {
    std::fs::read_to_string(storage_path)
        .with_context(|| format!("storage slots file exists but is unreadable: {}", storage_path.display()))?;
    serde_json::from_str::<Vec<StorageSlot>>(&std::fs::read_to_string(storage_path)?)
        .with_context(|| "invalid storage slots JSON")?;
}
// or preempt autoloading entirely if the sibling JSON is not wanted
let cfg = StorageConfiguration::default().with_autoload(false);

Try / catch

match StorageSlots::load_from_file(&path) {
    Err(e) if e.to_string().contains("failed to read storage slots") => {
        // Permission/transient FS issue: report the path and io cause, fix and retry once
        eprintln!("unreadable storage file at {}: {e}", path.display());
        return fix_permissions_and_retry(&path);
    }
    other => other,
}

Prevention

When it happens

Trigger: Explicitly configuring storage slots from a file whose permissions deny reading; or with autoloading enabled (the default), the sibling file <contract>-storage_slots.json exists but is unreadable (locked by another process, permissions, transient FS error) — note autoloading only engages when the file exists, so a plain missing file does not raise this.

Common situations: forc generates the storage-slots JSON alongside the binary but a CI step strips read permissions; Windows/macOS file locks while another build task still writes the JSON; a corrupted checkout or interrupted file sync; loading slots in a test harness that runs as a different user than the build step.

Related errors


AI-assisted analysis of FuelLabs/fuels-rs@d9a250a518 (2026-08-16). Data as JSON: /api/errors/8abc17b2ccffb5aa. Report an issue: GitHub.