zeroclaw-labs/zeroclaw · error

Failed to atomically replace config file: {e}

Error message

Failed to atomically replace config file: {e}

What it means

write_config_atomically writes the new config to a temp file, backs up the existing config, then publishes via fs::rename(temp, config). This error fires when that final rename fails; the code first removes the temp file and attempts to restore the backup over the config, so the on-disk config should be back to its pre-save state. The {e} suffix carries the underlying io::Error (permission denied, cross-device link, Windows sharing violation, target is a directory, …). Data is not lost — the previous config is preserved or restorable from the .bak file.

Source

Thrown at crates/zeroclaw-config/src/schema.rs:22993

    let had_existing_config = config_path.exists();
    if had_existing_config {
        fs::copy(config_path, &backup_path).await.with_context(|| {
            format!(
                "Failed to create config backup before atomic replace: {}",
                backup_path.display()
            )
        })?;
    }

    if let Err(e) = fs::rename(&temp_path, config_path).await {
        let _ = fs::remove_file(&temp_path).await;
        if had_existing_config && backup_path.exists() {
            fs::copy(&backup_path, config_path)
                .await
                .context("Failed to restore config backup")?;
        }
        anyhow::bail!("Failed to atomically replace config file: {e}");
    }

    #[cfg(unix)]
    {
        use std::{fs::Permissions, os::unix::fs::PermissionsExt};
        if let Err(err) = fs::set_permissions(config_path, Permissions::from_mode(0o600)).await {
            ::zeroclaw_log::record!(
                WARN,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
                    .with_outcome(::zeroclaw_log::EventOutcome::Unknown),
                &format!(
                    "Failed to harden config permissions to 0600 at {}: {}",
                    config_path.display().to_string(),
                    err
                )
            );
        }
    }

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Check the {e} suffix: 'permission denied' → fix ownership/permissions of the config path and its parent dir; 'cross-device link' → move temp creation onto the same filesystem (keep config_path local); sharing violation → close editors/AV/sync tools touching the file
  2. Verify free space on the filesystem holding the config
  3. If a .bak file remains next to the config, compare/restore it manually: cp config.toml.bak config.toml
  4. Retry the save once the locking process is gone (the previous config is intact, so this is safe)
Defensive patterns

Strategy: retry

Validate before calling

// preflight before save():
async fn can_replace(path: &Path) -> bool {
    let parent = match path.parent() { Some(p) => p, None => return false };
    std::fs::create_dir_all(parent).is_ok()
        && path.is_file() || !path.exists()          // not a directory
        && has_write_access(parent)                  // e.g. try File::create in parent
}

Try / catch

let mut attempt = 0;
loop {
    attempt += 1;
    match cfg.save().await {
        Ok(()) => break,
        Err(e) if attempt < 3 && e.to_string().contains("Failed to atomically replace config file") => {
            tokio::time::sleep(std::time::Duration::from_millis(250 * attempt as u64)).await; // Windows lock may clear
        }
        Err(e) => {
            eprintln!("config save failed after restore; a .bak may exist: {e:#}");
            break;
        }
    }
}

Prevention

When it happens

Trigger: Another process holds the config file open with an exclusive lock on Windows (editor, antivirus, sync client like Dropbox/OneDrive) when save()/save_dirty() runs; config_path is on a read-only or full filesystem; the rename crosses a filesystem boundary because the temp file's parent differs from the target's; config_path is actually a directory; insufficient permissions on the parent directory.

Common situations: Config auto-save while the user has config.toml open in Excel/locked editor on Windows; config directory inside a synced folder where the sync agent races renames; containers where the config dir is a mounted volume with odd rename semantics; running as a user without write permission on ~/.zeroclaw.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/b5b8c9ecbbdab9af. Report an issue: GitHub.