{"record":{"id":"e2cd29dd1bd73cab","repo":"tinyhumansai/openhuman","slug":"failed-to-atomically-persist-active-user-state","errorCode":null,"errorMessage":"Failed to atomically persist active user state {}: {error}","messagePattern":"Failed to atomically persist active user state (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/config/schema/load_user_state.rs","lineNumber":189,"sourceCode":"        .write(true)\n        .open(&temp_path)\n        .with_context(|| {\n            format!(\n                \"Failed to create temporary active user state: {}\",\n                temp_path.display()\n            )\n        })?;\n    temp_file\n        .write_all(toml_str.as_bytes())\n        .context(\"Failed to write temporary active user state\")?;\n    temp_file\n        .sync_all()\n        .context(\"Failed to fsync temporary active user state\")?;\n    drop(temp_file);\n\n    if let Err(error) = std::fs::rename(&temp_path, &path) {\n        let _ = std::fs::remove_file(&temp_path);\n        anyhow::bail!(\n            \"Failed to atomically persist active user state {}: {error}\",\n            path.display()\n        );\n    }\n\n    sync_directory(default_openhuman_dir)?;\n    tracing::debug!(user_id = %user_id, path = %path.display(), \"active user written\");\n    Ok(())\n}\n\n/// Removes the active user marker.  After this, the next config load will\n/// use the default (unauthenticated) openhuman directory.\npub fn clear_active_user(default_openhuman_dir: &Path) -> Result<()> {\n    let path = active_user_marker_path(default_openhuman_dir);\n    if path.exists() {\n        std::fs::remove_file(&path)\n            .with_context(|| format!(\"Failed to remove active user state: {}\", path.display()))?;\n        tracing::debug!(path = %path.display(), \"active user cleared\");","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/config/schema/load_user_state.rs#L171-L207","documentation":"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.","triggerScenarios":"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.","commonSituations":"Login/account-switch flows on Windows with AV or indexer locks; restricted home dirs in managed environments; concurrent login/account-switch writes.","solutions":["Retry the login/account switch — the write is idempotent (temp+rename) and transient locks usually clear.","Fix permissions on the OpenHuman dir and ensure the marker path is a file; close/stop whatever holds it open.","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."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Probe rename support in the OpenHuman dir before account switching\nlet dir = default_openhuman_dir();\nlet a = dir.join(format!(\".probe-{}a\", uuid::Uuid::new_v4()));\nlet b = dir.join(format!(\".probe-{}b\", uuid::Uuid::new_v4()));\nstd::fs::write(&a, b\"\")?;\nstd::fs::rename(&a, &b)?;\nlet _ = std::fs::remove_file(&b);","typeGuard":null,"tryCatchPattern":"for attempt in 0..3 {\n    match write_active_user_state(&user_id, &state).await {\n        Ok(()) => break,\n        Err(e) if e.to_string().contains(\"atomically persist active user state\") && attempt < 2 => {\n            tokio::time::sleep(std::time::Duration::from_millis(250)).await; // transient lock\n        }\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Exclude the OpenHuman dir from antivirus scanning on Windows","Ensure the marker path is a file with writable parent","Retry account switches after transient locks clear — the write is idempotent"],"tags":["config","user-state","atomic-write","filesystem","login"],"backgroundTag":"atomic-write-failed","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}