linera-io/linera-protocol · error

Failed to write updated server config

Error message

Failed to write updated server config

What it means

Panic when `Persist::persist(&mut server_config)` fails after `edit-shards` replaced `server_config.internal_network.shards`. Same mechanism as other persist failures: pretty JSON is written to `<path>.json.new` (0600), flushed, then atomically renamed over the config. If this fires, the old config is still on disk untouched — the shard edit simply did not land.

Source

Thrown at linera-service/src/server.rs:942

            }
        }

        ServerCommand::EditShards {
            server_config_path,
            num_shards,
            host,
            port,
            metrics_port,
        } => {
            let mut server_config =
                persistent::File::<ValidatorServerConfig>::read(&server_config_path)
                    .expect("Failed to read server config");
            let shards = generate_shard_configs(&num_shards, &host, &port, &metrics_port)
                .expect("Failed to generate shard configs");
            server_config.internal_network.shards = shards;
            Persist::persist(&mut server_config)
                .await
                .expect("Failed to write updated server config");
        }
    }
}

fn generate_shard_configs(
    num_shards: &str,
    host: &str,
    port: &str,
    metrics_port: &Option<String>,
) -> anyhow::Result<Vec<ShardConfig>> {
    let mut shards = Vec::new();
    let len = num_shards.len();
    let num_shards = num_shards
        .parse::<NonZeroU16>()
        .context("Failed to parse the number of shards")?;
    let pattern = "%".repeat(len);

    for i in 1u16..=num_shards.into() {

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Verify writability: `touch <dir>/.probe && rm <dir>/.probe`, and check `df -h <dir>`.
  2. Fix ownership of the config directory and any `*.json.new` leftovers, then rerun edit-shards.
  3. Mount the config volume read-write.
  4. Propagate the error in code instead of expect to see the exact io::Error.

Example fix

// before
Persist::persist(&mut server_config)
    .await
    .expect("Failed to write updated server config");
// after
Persist::persist(&mut server_config)
    .await
    .with_context(|| format!("unable to update server config {}", server_config_path.display()))?;
Defensive patterns

Strategy: validation

Validate before calling

let dir = server_config_path.parent().unwrap_or(Path::new("."));
anyhow::ensure!(!fs_err::metadata(dir)?.permissions().readonly(), "config dir is read-only: {}", dir.display());
let mut staging = server_config_path.clone();
staging.set_extension("json.new");
if staging.exists() {
    fs_err::remove_file(&staging)?;
}

Try / catch

if let Err(err) = Persist::persist(&mut server_config).await {
    eprintln!("error: failed to write updated server config {}: {err}", server_config_path.display());
    std::process::exit(1);
}

Prevention

When it happens

Trigger: Config directory read-only or unwritable by the editing user; ENOSPC while writing the staging file; leftover `server.json.new` owned by root from an earlier containerized run; rename blocked by unusual filesystem semantics.

Common situations: Editing shard configs on a deployment directory owned by root while running as another user; small tmpfs mounts in tests; CI containers with read-only layers where the config was copied in.

Related errors


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