tinyhumansai/openhuman · error

Failed to atomically replace config file: {e}

Error message

Failed to atomically replace config file: {e}

What it means

`Config::save` writes atomically: back up the existing file, write a temp file, `fs::rename` over the config path. If the final rename fails, the temp file is removed, the backup is copied back over the config path (surfacing `Failed to restore config backup` only if that also fails), and this bail reports the lost write. On-disk config is left at its pre-save state — the failure is a rollback, not corruption.

Source

Thrown at src/openhuman/config/schema/load/impl_load.rs:741

                fs::create_dir_all(parent_dir).await.with_context(|| {
                    format!(
                        "Failed to recreate config directory for atomic replace retry: {}",
                        parent_dir.display()
                    )
                })?;
                fs::rename(&temp_path, &self.config_path).await
            }
            result => result,
        };

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

        super::sync_directory(parent_dir).await?;

        Ok(())
    }
}

View on GitHub (pinned to 7491200858)

Solutions

  1. Retry the settings write — the failed attempt was rolled back, so state is consistent and the retry is safe.
  2. Remove the lock holder: close other processes using config.toml, exclude the config dir from antivirus scanning, verify write permissions on the directory.
  3. Move the workspace/config off read-only or aggressively-synced network storage if renames keep failing.
Defensive patterns

Strategy: retry

Validate before calling

// Probe writability of the config dir before a settings save
let dir = config.config_path.parent().unwrap();
let probe = dir.join(format!(".probe-{}", uuid::Uuid::new_v4()));
tokio::fs::write(&probe, b"").await?;
let _ = tokio::fs::remove_file(&probe).await;

Try / catch

match config.save().await {
    Err(e) if e.to_string().contains("atomically replace config file") => {
        // the save was rolled back — on-disk config is unchanged, safe to retry
        tokio::time::sleep(std::time::Duration::from_millis(500)).await;
        config.save().await?
    }
    other => other?,
}

Prevention

When it happens

Trigger: Rename failures on the config directory: permission/ACL restrictions, config.toml held open by another process (Windows sharing violation, editor, antivirus, backup tool), a read-only or network-synced filesystem, or the temp file and target crossing a filesystem boundary.

Common situations: Windows machines with antivirus/backup tools touching config.toml during saves; configs on network drives or sync mounts (OneDrive/Dropbox) where renames fail; two core processes writing config concurrently.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/e234169a4dd9d2d3. Report an issue: GitHub.