tinyhumansai/openhuman · error

Failed to atomically persist active user state {}: {error}

Error message

Failed to atomically persist active user state {}: {error}

What it means

The active-user marker is written atomically: create a temp file, write the TOML, `sync_all` (fsync), rename over the target path, then fsync the directory. This bail fires when the rename step fails — the temp file is removed, so no partial state is left; the previously active user marker (if any) remains in effect.

Source

Thrown at src/openhuman/config/schema/load_user_state.rs:189

        .write(true)
        .open(&temp_path)
        .with_context(|| {
            format!(
                "Failed to create temporary active user state: {}",
                temp_path.display()
            )
        })?;
    temp_file
        .write_all(toml_str.as_bytes())
        .context("Failed to write temporary active user state")?;
    temp_file
        .sync_all()
        .context("Failed to fsync temporary active user state")?;
    drop(temp_file);

    if let Err(error) = std::fs::rename(&temp_path, &path) {
        let _ = std::fs::remove_file(&temp_path);
        anyhow::bail!(
            "Failed to atomically persist active user state {}: {error}",
            path.display()
        );
    }

    sync_directory(default_openhuman_dir)?;
    tracing::debug!(user_id = %user_id, path = %path.display(), "active user written");
    Ok(())
}

/// Removes the active user marker.  After this, the next config load will
/// use the default (unauthenticated) openhuman directory.
pub fn clear_active_user(default_openhuman_dir: &Path) -> Result<()> {
    let path = active_user_marker_path(default_openhuman_dir);
    if path.exists() {
        std::fs::remove_file(&path)
            .with_context(|| format!("Failed to remove active user state: {}", path.display()))?;
        tracing::debug!(path = %path.display(), "active user cleared");

View on GitHub (pinned to 7491200858)

Solutions

  1. Retry the login/account switch — the write is idempotent (temp+rename) and transient locks usually clear.
  2. Fix permissions on the OpenHuman dir and ensure the marker path is a file; close/stop whatever holds it open.
  3. If persistent, remove the stale marker file and retry — the next config load falls back to the default unauthenticated openhuman directory, and a fresh marker can then be written.
Defensive patterns

Strategy: retry

Validate before calling

// Probe rename support in the OpenHuman dir before account switching
let dir = default_openhuman_dir();
let a = dir.join(format!(".probe-{}a", uuid::Uuid::new_v4()));
let b = dir.join(format!(".probe-{}b", uuid::Uuid::new_v4()));
std::fs::write(&a, b"")?;
std::fs::rename(&a, &b)?;
let _ = std::fs::remove_file(&b);

Try / catch

for attempt in 0..3 {
    match write_active_user_state(&user_id, &state).await {
        Ok(()) => break,
        Err(e) if e.to_string().contains("atomically persist active user state") && attempt < 2 => {
            tokio::time::sleep(std::time::Duration::from_millis(250)).await; // transient lock
        }
        Err(e) => return Err(e),
    }
}

Prevention

When it happens

Trigger: Rename failures in the OpenHuman dir: permission restrictions, the marker path held open or locked (Windows sharing violation, antivirus), a read-only home directory, or the target path existing as a directory.

Common situations: Login/account-switch flows on Windows with AV or indexer locks; restricted home dirs in managed environments; concurrent login/account-switch writes.

Related errors


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