linera-io/linera-protocol · error
Unable to open server config file
Error message
Unable to open server config file
What it means
Panic when `make_server_config` fails during `server generate`. That function generates the validator keypair and calls `persistent::File::new(path, ValidatorServerConfig{...})`, which opens/creates the file named by `server_config_path` from the options TOML with read+write+create (mode 0600 on Unix), takes a non-blocking exclusive flock, and immediately performs an atomic save. So 'open' in the message also covers lock contention and initial-save I/O errors, not just opening the file.
Source
Thrown at linera-service/src/server.rs:901
.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,
CommitteeConfig {
validators: config_validators,
},
)
.expect("Unable to open committee configuration");View on GitHub (pinned to 6c226ddcb3)
Solutions
- Create the parent directory of `server_config_path` (`mkdir -p $(dirname <path>)`) and make it writable by the current user.
- Stop any other linera-service process using that config file (check with `lsof <path>` or `fuser -v <path>`), then rerun generate.
- Delete a stale `<path>.json.new` staging file left by a crashed run, especially one owned by root.
- Ensure the config volume is mounted rw and owned by the container user.
- In code, handle the Result: `make_server_config(...).with_context(...)?` instead of `.expect(...)`.
Example fix
// before
let mut server = make_server_config(&path, &mut rng, options)
.expect("Unable to open server config file");
// after
if let Some(parent) = path.parent().filter(|p| !p.as_os_str().is_empty()) {
std::fs::create_dir_all(parent)
.with_context(|| format!("cannot create config dir {}", parent.display()))?;
}
let mut server = make_server_config(&path, &mut rng, options)
.with_context(|| format!("unable to create/lock server config {}", path.display()))?; Defensive patterns
Strategy: validation
Validate before calling
let parent = path.parent().filter(|p| !p.as_os_str().is_empty()).context("invalid server_config_path")?;
anyhow::ensure!(parent.is_dir(), "config directory does not exist: {}", parent.display());
let probe = parent.join(".linera_probe");
std::fs::write(&probe, b"")?;
std::fs::remove_file(&probe)?; Try / catch
let mut server = match make_server_config(&path, &mut rng, options) {
Ok(server) => server,
Err(err) => {
eprintln!("error: unable to open server config {}: {err:#}", path.display());
std::process::exit(1);
}
}; Prevention
- Pre-create config directories during provisioning before running `server generate`.
- Never run generate against config files a live validator holds; the flock is exclusive and taken non-blocking.
- Run the binary as the user that will own the config files to avoid root-owned 0600 files and staging leftovers.
- Keep `server_config_path` absolute in the options TOML to avoid cwd-dependent behavior.
When it happens
Trigger: `server_config_path` in the validator options TOML points into a directory that does not exist or is unwritable (ENOENT/EACCES on create); another linera-service process (running validator or a second `generate`) holds the exclusive flock on the file; the initial save inside `File::new` fails because the directory or a leftover `<path>.json.new` staging file is unwritable or the disk is full.
Common situations: Options TOML with `server_config_path = "server_1.json"` while generate runs from a different cwd; first deployment where the config directory (e.g. /etc/linera) was never created; running generate while validators are up holding their configs; Docker/Kubernetes volumes mounted read-only or owned by root while the process runs as non-root.
Related errors
- Unable to open committee configuration
- Failed to write updated server config
- Cannot process inbox for follow-only chain {chain_id}. Use `
- failed to load contract bytecode from {contract:?}: {e}
- failed to load service bytecode from {service:?}: {e}
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/d8040fd7b820db67.
Report an issue: GitHub.