affaan-m/ECC · error · anyhow::Error

Completed sessions cannot be resumed

Error message

Completed sessions cannot be resumed: {}

What it means

resume_session_with_program refuses to resume a session whose state is Completed. Completed sessions represent finished work; resuming them is unsupported because their program has already terminated successfully. The caller (resume_session) surfaces this as an error to the user.

Solutions

  1. Check session.state before resuming and skip or create a new session if Completed
  2. Start a new session instead of resuming the completed one
  3. Refresh session state from the DB before offering the resume action in UIs/automation

Example fix

// before
resume_session(db, id)?;
// after
let session = resolve_session(db, id)?;
if session.state == SessionState::Completed {
    let new_id = create_session(db, &session.config)?;
} else {
    resume_session(db, id)?;
}
Defensive patterns

Strategy: validation

Validate before calling

let session = resolve_session(db, id)?;
if session.state == SessionState::Completed {
    return Err(format!("session {} already completed; create a new session instead", id));
}

Type guard

fn is_resumable(s: &Session) -> bool { s.state != SessionState::Completed && s.state != SessionState::Running }

Try / catch

match resume_session(db, id) {
    Err(e) if e.to_string().contains("Completed sessions cannot be resumed") => start_new_session(db, &config),
    other => other,
}

Prevention

When it happens

Trigger: Calling resume_session(id) or tests like resume_session_requeues_failed_session against a session whose stored state in the StateStore equals SessionState::Completed.

Common situations: User clicks 'resume' on a session in the UI that already finished; automation re-submitting a session id after it completed; stale client state showing the session as Failed or Pending when the DB says Completed.

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


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/ca8fcb1e9bc518d8. Report an issue: GitHub.

Appendix: source

Thrown at ecc2/src/session/manager.rs:1516

        .ok_or_else(|| anyhow::anyhow!("Session not found: {session_id}"))?;

    ToolLogger::new(db).query(&session.id, page, page_size)
}

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")?,

View on GitHub (pinned to 8321021c54)