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

  1. Create the parent directory of `server_config_path` (`mkdir -p $(dirname <path>)`) and make it writable by the current user.
  2. Stop any other linera-service process using that config file (check with `lsof <path>` or `fuser -v <path>`), then rerun generate.
  3. Delete a stale `<path>.json.new` staging file left by a crashed run, especially one owned by root.
  4. Ensure the config volume is mounted rw and owned by the container user.
  5. 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

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


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