{"record":{"id":"0fe1ee7c1525cbcb","repo":"affaan-m/ECC","slug":"session-is-already-running","errorCode":null,"errorMessage":"Session is already running: {}","messagePattern":"Session is already running: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"ecc2/src/session/manager.rs","lineNumber":1520,"sourceCode":"\npub async fn resume_session(db: &StateStore, cfg: &Config, id: &str) -> Result<String> {\n    resume_session_with_program(db, cfg, id, None).await\n}\n\nasync fn resume_session_with_program(\n    db: &StateStore,\n    _cfg: &Config,\n    id: &str,\n    runner_executable_override: Option<&Path>,\n) -> Result<String> {\n    let session = resolve_session(db, id)?;\n\n    if session.state == SessionState::Completed {\n        anyhow::bail!(\"Completed sessions cannot be resumed: {}\", session.id);\n    }\n\n    if session.state == SessionState::Running {\n        anyhow::bail!(\"Session is already running: {}\", session.id);\n    }\n\n    db.update_state_and_pid(&session.id, &SessionState::Pending, None)?;\n    if let Some(worktree) = session.worktree.as_ref() {\n        if let Err(error) = worktree::sync_shared_dependency_dirs(worktree) {\n            tracing::warn!(\n                \"Shared dependency cache sync warning for resumed session {}: {error}\",\n                session.id\n            );\n        }\n    }\n    let runner_executable = match runner_executable_override {\n        Some(program) => program.to_path_buf(),\n        None => std::env::current_exe().context(\"Failed to resolve ECC executable path\")?,\n    };\n    spawn_session_runner_for_program(\n        &session.task,\n        &session.id,","sourceCodeStart":1502,"sourceCodeEnd":1538,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/session/manager.rs#L1502-L1538","documentation":"The resume_session_with_program function checks if a session is already in the Running state before attempting to resume it. Resuming a running session would spawn a duplicate process and corrupt the state machine. The check fires after the Completed-state guard and before any DB state mutation.","triggerScenarios":"Calling resume_session on a session whose state is SessionState::Running — the agent process is still active.","commonSituations":"Race condition where two callers try to resume the same session. Resume button clicked twice. Session process is alive but the UI lost track of its state.","solutions":["Check session.state before calling resume — if Running, the session is already active","Use a lock or compare-and-exchange on session state to prevent concurrent resume attempts","Call stop_session first if you need to restart, then resume","Track running sessions in the caller and deduplicate resume requests"],"exampleFix":"// before\nresume_session(db, cfg, &id).await?;\n\n// after\nlet session = resolve_session(db, &id)?;\nif session.state == SessionState::Running {\n    tracing::info!(\"session {} is already running\", id);\n    return Ok(session.id);\n}\nresume_session(db, cfg, &id).await?;","handlingStrategy":"type-guard","validationCode":"let session = resolve_session(db, id)?;\nif session.state == SessionState::Running {\n    tracing::info!(\"session {} is already running, skipping resume\", id);\n    return Ok(session.id);\n}","typeGuard":"fn is_resumable(session: &Session) -> bool {\n    !matches!(session.state, SessionState::Completed | SessionState::Running)\n}","tryCatchPattern":null,"preventionTips":["Use a session-state lock or CAS to prevent concurrent resume calls","Track running sessions in the caller and deduplicate resume requests","Idempotently handle 'already running' as success rather than an error"],"tags":["sessions","lifecycle","resume","state-machine","concurrency"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}