linera-io/linera-protocol · error · std::io::Error

file is empty or does not exist: {}

Error message

file is empty or does not exist: {}

What it means

`linera_persistent::file::File::read` loads a JSON-persisted value (wallets, configs) from a path and is the strict variant of `read_or_create`: if the file is missing or zero bytes, the value-creation closure itself returns `std::io::ErrorKind::NotFound` with this message. The file is flock-locked and atomically rewritten through a `<path>.json.new` staging file, so an empty main file usually means it was never written or was truncated externally.

Source

Thrown at linera-persistent/src/file.rs:155

                    .write(true)
                    .create(true)
                    .open(path)?,
            )
            .map_err(|source| Error::Lock {
                path: path.into(),
                source,
            })?,
            path: path.into(),
            value,
        };
        this.save()?;
        Ok(this)
    }

    /// Reads the value from a file at `path`, returning an error if it does not exist.
    pub fn read(path: &Path) -> Result<Self, Error> {
        Self::read_or_create(path, || {
            Err(std::io::Error::new(
                std::io::ErrorKind::NotFound,
                format!("file is empty or does not exist: {}", path.display()),
            )
            .into())
        })
    }

    /// Reads the value from a file at `path`, calling the `value` function to create it
    /// if it does not exist. If it does exist, `value` will not be called.
    pub fn read_or_create(
        path: &Path,
        value: impl FnOnce() -> Result<T, Error>,
    ) -> Result<Self, Error> {
        let lock = Lock::new(open_options().read(true).open(path)?)?;
        let mut reader = io::BufReader::new(&lock.0);
        let file_is_empty = reader.fill_buf()?.is_empty();

        let me = Self {

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Verify the path exists and is non-empty before reading.
  2. Create the file first with the tool's creation command, or use `File::read_or_create(path, || Ok(default_value))` instead of `File::read`.
  3. If the file was expected to exist, restore it from backup — an empty file means there is no state to recover via `read`.
  4. Check for a leftover `<path>.json.new` staging file if a crash interrupted a save.

Example fix

// before
let wallet = File::<Wallet>::read(&path)?; // NotFound if path missing/empty

// after
let wallet = File::<Wallet>::read_or_create(&path, || Ok(Wallet::default()))?;
Defensive patterns

Strategy: validation

Validate before calling

use std::path::Path;
fn readable(p: &Path) -> bool {
    std::fs::metadata(p).map(|m| m.len() > 0).unwrap_or(false)
}

Try / catch

match File::<T>::read(&path) { Ok(f) => f, Err(e) if e.to_string().contains("file is empty or does not exist") => File::read_or_create(&path, T::default)?, Err(e) => return Err(e.into()) }

Prevention

When it happens

Trigger: Passing a wallet/config path that does not exist (typo, wrong `--wallet` argument); pointing at a fresh directory without running the creation step; a zero-byte file left by manual truncation or an interrupted external process.

Common situations: First run of a tool that expects an existing wallet instead of creating one; scripts referencing `$HOME` under a different user; renamed or moved data directories; CI starting with a clean environment.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/918b93438bc16278. Report an issue: GitHub.