{"record":{"id":"7ed948d4e27b60eb","repo":"linera-io/linera-protocol","slug":"unable-to-write-server-config-file","errorCode":null,"errorMessage":"Unable to write server config file","messagePattern":"Unable to write server config file","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-service/src/server.rs","lineNumber":904,"sourceCode":"        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\");\n                Persist::persist(&mut config)\n                    .await\n                    .expect(\"Unable to write committee description\");","sourceCodeStart":886,"sourceCodeEnd":922,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-service/src/server.rs#L886-L922","documentation":"Panic when `Persist::persist(&mut server)` fails while writing a validator server config created by `server generate`. `File::persist` serializes the value to pretty JSON into a staging file `<path>.json.new` (mode 0600), flushes it, then atomically renames it over `<path>`; the staging file is removed on serialization or write errors. Failure means the staging file could not be opened/written, serialization failed, or the final rename failed.","triggerScenarios":"Config directory not writable or on a read-only mount so `open(\"<path>.json.new\")` fails with EACCES/EROFS; disk full (ENOSPC) while flushing the staging file; leftover `.json.new` owned by another user; rename failing on exotic filesystems (NFS/FUSE); JSON serialization of ValidatorServerConfig failing after an internal schema change.","commonSituations":"Kubernetes pod with the config path on a read-only ConfigMap mount; small disks on CI or test VMs; Docker image run once as root then as non-root, leaving a root-owned staging file; network volumes where same-directory rename semantics differ.","solutions":["Check writability and space: `df -h <dir>` and `touch <dir>/.probe` (then remove the probe).","Remount the volume read-write, or move the config path to a writable directory.","Free disk space or enlarge the volume if the failure is ENOSPC.","Remove stale root-owned `<path>.json.new` leftovers before rerunning.","Replace `.expect` with `.with_context(...)?` in embedded code to surface the underlying io::Error."],"exampleFix":"// before\nPersist::persist(&mut server)\n    .await\n    .expect(\"Unable to write server config file\");\n// after\nPersist::persist(&mut server)\n    .await\n    .with_context(|| format!(\"unable to write server config {}\", path.display()))?;","handlingStrategy":"validation","validationCode":"let dir = path.parent().unwrap_or(Path::new(\".\"));\nanyhow::ensure!(!fs_err::metadata(dir)?.permissions().readonly(), \"config dir is read-only: {}\", dir.display());\nlet staging = {\n    let mut p = path.clone();\n    p.set_extension(\"json.new\");\n    p\n};\nif staging.exists() {\n    fs_err::remove_file(&staging)?;\n}","typeGuard":null,"tryCatchPattern":"if let Err(err) = Persist::persist(&mut server).await {\n    eprintln!(\"error: unable to write server config {}: {err}\", path.display());\n    std::process::exit(1);\n}","preventionTips":["Mount config volumes read-write and monitor disk usage where validator configs live.","Always run generate as the same (non-root) user so 0600 files and staging leftovers stay owned by it.","Avoid NFS/FUSE for the config path; the write path relies on same-directory atomic rename.","Treat `.json.new` files found during provisioning as crash residue and clean them."],"tags":["rust","file-io","disk-full","atomic-write","permissions"],"backgroundTag":"file-write-failed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}