{"record":{"id":"5c17c2af02c0e8d1","repo":"tinyhumansai/openhuman","slug":"agent-replay-list-active-runs-failed-e","errorCode":null,"errorMessage":"[agent] replay list_active_runs failed: {e}","messagePattern":"\\[agent\\] replay list_active_runs failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/openhuman/agent/tinyagents/replay/ops.rs","lineNumber":154,"sourceCode":"pub(crate) async fn list_active_runs(\n    workspace: &Path,\n    thread_id: Option<&str>,\n    root_run_id: Option<&str>,\n) -> anyhow::Result<Vec<HarnessRunStatus>> {\n    log::debug!(\n        \"[agent] replay list_active_runs thread_id={:?} root_run_id={:?}\",\n        thread_id,\n        root_run_id\n    );\n    let stores = open_session_stores(workspace);\n    let store = FileStatusStore::new(stores.kv);\n\n    let base = match (thread_id, root_run_id) {\n        (Some(thread), _) => store.list_by_thread(thread).await,\n        (None, Some(root)) => store.list_by_root(root).await,\n        (None, None) => store.list_active().await,\n    }\n    .map_err(|e| anyhow::anyhow!(\"[agent] replay list_active_runs failed: {e}\"))?;\n\n    let mut runs: Vec<HarnessRunStatus> = base.into_iter().filter(is_active).collect();\n    // If a caller supplied BOTH a thread and a root, the thread query drove the\n    // base list; narrow it to the requested root as well.\n    if thread_id.is_some() {\n        if let Some(root) = root_run_id {\n            runs.retain(|s| s.root_run_id.as_str() == root);\n        }\n    }\n\n    log::debug!(\"[agent] replay list_active_runs returned={}\", runs.len());\n    Ok(runs)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::sync::Arc;","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/src/openhuman/agent/tinyagents/replay/ops.rs#L136-L172","documentation":"Active-run listing failed: one of list_by_thread / list_by_root / list_active on FileStatusStore errored (replay/ops.rs:154). The thread/root/active query selection happens before the store call, so the failure is in reading the kv status store itself — lock contention, missing or corrupt workspace store, permissions.","triggerScenarios":"Listing active runs while another process holds the kv store lock; workspace directory moved or restored; corrupted status store; permission changes on the workspace tree.","commonSituations":"Dashboards/UI polling active runs against a busy core; resumed/recovered workspaces after a crash; shared workspaces across processes.","solutions":["Verify the workspace path is accessible and writable","Retry the listing after the concurrent writer finishes — busy locks are transient","If the status store is corrupted (crash mid-write), rebuild the workspace session stores","Run listing queries from the process that owns the workspace"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if std::fs::metadata(workspace).is_err() {\n    anyhow::bail!(\"workspace missing at {} — cannot list runs\", workspace.display());\n}","typeGuard":null,"tryCatchPattern":"// Listing is poll-shaped: degrade gracefully instead of failing the caller\nmatch list_active_runs(workspace, thread_id, root_run_id).await {\n    Ok(runs) => runs,\n    Err(e) if is_transient_store_error(&e) => Vec::new(), // retry next poll tick\n    Err(e) => return Err(e),\n}","preventionTips":["Run listing polls from the workspace-owning process","Distinguish transient (busy/lock) from permanent (missing/corrupt) store errors in handlers","Avoid listing while restoring or copying a workspace directory"],"tags":["rust","replay","storage","io","listing"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}