tinyhumansai/openhuman · error

Failed to atomically persist active workspace marker {}: {er

Error message

Failed to atomically persist active workspace marker {}: {error}

What it means

The active-workspace marker is persisted atomically: serialize state, write to a UUID-suffixed temp file in the default config dir, then `fs::rename` over the marker path (followed by a directory fsync). This bail fires when the rename step fails — the temp file is removed first, so no half-written marker is left; the previous marker, if any, remains authoritative.

Source

Thrown at src/openhuman/config/schema/load/dirs.rs:233

        config_dir: config_dir.to_string_lossy().into_owned(),
    };
    let serialized =
        toml::to_string_pretty(&state).context("Failed to serialize active workspace marker")?;

    let temp_path = default_config_dir.join(format!(
        ".{ACTIVE_WORKSPACE_STATE_FILE}.tmp-{}",
        uuid::Uuid::new_v4()
    ));
    fs::write(&temp_path, serialized).await.with_context(|| {
        format!(
            "Failed to write temporary active workspace marker: {}",
            temp_path.display()
        )
    })?;

    if let Err(error) = fs::rename(&temp_path, &state_path).await {
        let _ = fs::remove_file(&temp_path).await;
        anyhow::bail!(
            "Failed to atomically persist active workspace marker {}: {error}",
            state_path.display()
        );
    }

    super::sync_directory(&default_config_dir).await?;
    Ok(())
}

pub(crate) fn resolve_config_dir_for_workspace(workspace_dir: &Path) -> (PathBuf, PathBuf) {
    let workspace_config_dir = workspace_dir.to_path_buf();
    if workspace_config_dir.join("config.toml").exists() {
        return (
            workspace_config_dir.clone(),
            workspace_config_dir.join("workspace"),
        );
    }

View on GitHub (pinned to 7491200858)

Solutions

  1. Retry the workspace switch — the write is idempotent (temp+rename) and transient locks usually clear.
  2. Check permissions on `~/.openhuman`, ensure the marker path is a file not a directory, and close/stop processes holding it open.
  3. On Windows, exclude the `.openhuman` directory from real-time antivirus scanning or retry after the scan completes.
Defensive patterns

Strategy: retry

Validate before calling

// Probe that the config dir supports write+rename before switching workspaces
let dir = default_config_dir();
let a = dir.join(format!(".probe-{}a", uuid::Uuid::new_v4()));
let b = dir.join(format!(".probe-{}b", uuid::Uuid::new_v4()));
tokio::fs::write(&a, b"").await?;
tokio::fs::rename(&a, &b).await?;
let _ = tokio::fs::remove_file(&b).await;

Try / catch

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

Prevention

When it happens

Trigger: Rename failures in the config dir: the target held open/locked (Windows sharing violation, antivirus, indexer), `~/.openhuman` read-only or ACL-restricted, the marker path itself being a directory, or temp and target ending up on different filesystems.

Common situations: Windows machines with antivirus or search indexer briefly locking the marker; permission-restricted home dirs in managed environments; two processes switching workspaces at the same instant.

Related errors


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