linera-io/linera-protocol · error
Unable to read validator options file
Error message
Unable to read validator options file
What it means
Panic raised by `linera-service server generate` when `fs_err::tokio::read_to_string` fails on one of the files passed via `--validators <path>`. The file must exist, be readable by the current user, and contain valid UTF-8, because it is immediately parsed as TOML into ValidatorOptions. Since `.expect()` is used, the whole CLI command aborts with this message plus the underlying std::io::Error (fs_err decorates it with the failing path).
Source
Thrown at linera-service/src/server.rs:896
store_config
.run_with_storage(wasm_runtime, allow_application_logs, cache_sizes, job)
.boxed()
.await
.unwrap()
.unwrap();
}
ServerCommand::Generate {
validators,
committee,
testing_prng_seed,
} => {
let mut config_validators = Vec::new();
let mut rng = Box::<dyn CryptoRng>::from(testing_prng_seed);
for options_path in validators {
let options_string = fs_err::tokio::read_to_string(options_path)
.await
.expect("Unable to read validator options file");
let options: ValidatorOptions = toml::from_str(&options_string)
.unwrap_or_else(|_| panic!("Invalid options file format: \n {options_string}"));
let path = options.server_config_path.clone();
let mut server = make_server_config(&path, &mut rng, options)
.expect("Unable to open server config file");
Persist::persist(&mut server)
.await
.expect("Unable to write server config file");
info!("Wrote server config {}", path.to_str().unwrap());
println!(
"{},{}",
server.validator.public_key, server.validator.account_key
);
config_validators.push(Persist::into_value(server).validator);
}
if let Some(committee) = committee {
let mut config = persistent::File::new(
&committee,View on GitHub (pinned to 6c226ddcb3)
Solutions
- Verify the file exists and is readable (`ls -l <path>`, `cat <path>`) and contains readable TOML text.
- Pass an absolute path, or re-run the command from the directory the relative path is based on.
- Fix permissions or ownership: `chmod +r <file>` / `chown $(id -un) <file>`, and confirm the container user can read it.
- If embedding this code path, replace `.expect(...)` with `.with_context(...)?` so the decorated io::Error is reported instead of a panic.
Example fix
// before
let options_string = fs_err::tokio::read_to_string(options_path)
.await
.expect("Unable to read validator options file");
// after
let options_string = fs_err::tokio::read_to_string(&options_path)
.await
.with_context(|| format!("unable to read validator options file: {}", options_path.display()))?; Defensive patterns
Strategy: validation
Validate before calling
let meta = fs_err::metadata(&options_path)
.with_context(|| format!("validator options file not found: {}", options_path.display()))?;
anyhow::ensure!(meta.is_file(), "not a regular file: {}", options_path.display());
let bytes = fs_err::read(&options_path)?;
anyhow::ensure!(std::str::from_utf8(&bytes).is_ok(), "validator options file is not valid UTF-8"); Try / catch
let options_string = match fs_err::tokio::read_to_string(&options_path).await {
Ok(s) => s,
Err(err) => {
eprintln!("error: unable to read validator options file: {err}");
std::process::exit(1);
}
}; Prevention
- Always pass --validators an absolute path, or resolve it against a documented working directory in scripts.
- Check the file with `cat`/`file` before invoking generate; it must be readable UTF-8 TOML.
- In deployment manifests, ensure the process user owns or can read the options files.
- Regenerate the options file from the provisioning template whenever this panic appears, instead of retrying blindly.
When it happens
Trigger: Running `linera-service server generate --validators <path> ...` where the path does not exist, is a directory, has no read permission, or contains non-UTF-8 bytes; also when a relative path is resolved against a different working directory than the one assumed.
Common situations: Typo'd or relative --validators path used from the wrong cwd; options file copied from another machine/user without read permission; container running as non-root against a root-owned file; provisioning scripts referencing a temp options file that was already cleaned up.
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
- failed to load contract bytecode from {contract:?}: {e}
- failed to load service bytecode from {service:?}: {e}
- failed to load data blob bytes from {blob_path:?}: {e}
- failed to read SNAP file {path:?}: {e}
- Unable to open server config file
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/f483e23455039317.
Report an issue: GitHub.