{"record":{"id":"d8040fd7b820db67","repo":"linera-io/linera-protocol","slug":"unable-to-open-server-config-file","errorCode":null,"errorMessage":"Unable to open server config file","messagePattern":"Unable to open server config file","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-service/src/server.rs","lineNumber":901,"sourceCode":"                .unwrap();\n        }\n\n        ServerCommand::Generate {\n            validators,\n            committee,\n            testing_prng_seed,\n        } => {\n            let mut config_validators = Vec::new();\n            let mut rng = Box::<dyn CryptoRng>::from(testing_prng_seed);\n            for options_path in validators {\n                let options_string = fs_err::tokio::read_to_string(options_path)\n                    .await\n                    .expect(\"Unable to read validator options file\");\n                let options: ValidatorOptions = toml::from_str(&options_string)\n                    .unwrap_or_else(|_| panic!(\"Invalid options file format: \\n {options_string}\"));\n                let path = options.server_config_path.clone();\n                let mut server = make_server_config(&path, &mut rng, options)\n                    .expect(\"Unable to open server config file\");\n                Persist::persist(&mut server)\n                    .await\n                    .expect(\"Unable to write server config file\");\n                info!(\"Wrote server config {}\", path.to_str().unwrap());\n                println!(\n                    \"{},{}\",\n                    server.validator.public_key, server.validator.account_key\n                );\n                config_validators.push(Persist::into_value(server).validator);\n            }\n            if let Some(committee) = committee {\n                let mut config = persistent::File::new(\n                    &committee,\n                    CommitteeConfig {\n                        validators: config_validators,\n                    },\n                )\n                .expect(\"Unable to open committee configuration\");","sourceCodeStart":883,"sourceCodeEnd":919,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-service/src/server.rs#L883-L919","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","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(...)`."],"exampleFix":"// before\nlet mut server = make_server_config(&path, &mut rng, options)\n    .expect(\"Unable to open server config file\");\n// after\nif let Some(parent) = path.parent().filter(|p| !p.as_os_str().is_empty()) {\n    std::fs::create_dir_all(parent)\n        .with_context(|| format!(\"cannot create config dir {}\", parent.display()))?;\n}\nlet mut server = make_server_config(&path, &mut rng, options)\n    .with_context(|| format!(\"unable to create/lock server config {}\", path.display()))?;","handlingStrategy":"validation","validationCode":"let parent = path.parent().filter(|p| !p.as_os_str().is_empty()).context(\"invalid server_config_path\")?;\nanyhow::ensure!(parent.is_dir(), \"config directory does not exist: {}\", parent.display());\nlet probe = parent.join(\".linera_probe\");\nstd::fs::write(&probe, b\"\")?;\nstd::fs::remove_file(&probe)?;","typeGuard":null,"tryCatchPattern":"let mut server = match make_server_config(&path, &mut rng, options) {\n    Ok(server) => server,\n    Err(err) => {\n        eprintln!(\"error: unable to open server config {}: {err:#}\", path.display());\n        std::process::exit(1);\n    }\n};","preventionTips":["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."],"tags":["rust","file-io","permissions","file-lock","cli"],"backgroundTag":"file-permission-denied","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}