affaan-m/ECC · error · anyhow::Error
Session is already running
Error message
Session is already running: {} What it means
resume_session_with_program refuses to resume a session that is already in the Running state, since resuming would start a second concurrent execution of the same session. The guard prevents duplicate processes and pid/state corruption.
Solutions
- Check the session state and skip if already Running
- Wait for the running session to finish before resuming
- If the state is stuck as Running due to a crash, reset state (update_state_and_pid) after verifying no live process holds the pid
- Serialize resume calls with a lock so only one worker resumes a given session
Example fix
// before
resume_session(db, id)?;
// after
let session = resolve_session(db, id)?;
if session.state != SessionState::Running {
resume_session(db, id)?;
} else {
eprintln!("session {} already running; skipping", id);
} Defensive patterns
Strategy: validation
Validate before calling
let session = resolve_session(db, id)?;
if session.state == SessionState::Running {
return Err(format!("session {} is already running", id));
} Type guard
fn is_stopped(s: &Session) -> bool { !matches!(s.state, SessionState::Running) } Try / catch
match resume_session(db, id) {
Err(e) if e.to_string().contains("already running") => { /* poll until finished or skip */ }
other => other?,
} Prevention
- Use a lock/claim so only one worker resumes a session at a time
- Detect crashed runners and reset stale Running states via update_state_and_pid
- Debounce resume triggers in UIs
When it happens
Trigger: Calling resume_session(id) (or via resume_session_requeues_failed_session) while the session's stored state is SessionState::Running.
Common situations: Double-clicking a resume button; a scheduler re-triggering resume while the first process is still alive; a crashed process left the pid registered and the state Running; concurrent automation workers both trying to resume the same session.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Completed sessions cannot be resumed
- baseline is not the active harness configuration
- Cannot delete active session
- Cannot merge active session
- Cannot rebase active session
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/0fe1ee7c1525cbcb.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/session/manager.rs:1520
pub async fn resume_session(db: &StateStore, cfg: &Config, id: &str) -> Result<String> {
resume_session_with_program(db, cfg, id, None).await
}
async fn resume_session_with_program(
db: &StateStore,
_cfg: &Config,
id: &str,
runner_executable_override: Option<&Path>,
) -> Result<String> {
let session = resolve_session(db, id)?;
if session.state == SessionState::Completed {
anyhow::bail!("Completed sessions cannot be resumed: {}", session.id);
}
if session.state == SessionState::Running {
anyhow::bail!("Session is already running: {}", session.id);
}
db.update_state_and_pid(&session.id, &SessionState::Pending, None)?;
if let Some(worktree) = session.worktree.as_ref() {
if let Err(error) = worktree::sync_shared_dependency_dirs(worktree) {
tracing::warn!(
"Shared dependency cache sync warning for resumed session {}: {error}",
session.id
);
}
}
let runner_executable = match runner_executable_override {
Some(program) => program.to_path_buf(),
None => std::env::current_exe().context("Failed to resolve ECC executable path")?,
};
spawn_session_runner_for_program(
&session.task,
&session.id,View on GitHub (pinned to 8321021c54)