Hmbown/CodeWhale · error

Cloud job is and cannot be run; confirm it first with…

Error message

Cloud job {id} is {} and cannot be run; confirm it first with `/dispatch confirm {id}`.

What it means

run_confirmed_job only drives cloud jobs in Launching or Running status. Any other status (e.g. pending confirmation, done, cancelled, failed) cannot be run, and the error points at the `/dispatch confirm <id>` command as the required gate.

Solutions

  1. Run `/dispatch confirm <id>` first, then call run again
  2. Check the job status with the dispatch overview/list command before running
  3. Create a new job if the old one is terminal (done/cancelled/failed)

Example fix

// before
run_confirmed_job(store, "job42", ...)
// after
// confirm first via the command surface:
// /dispatch confirm job42
run_confirmed_job(store, "job42", ...)
Defensive patterns

Strategy: validation

Validate before calling

let job = store.load(id)?;
if !matches!(job.status, CloudJobStatus::Launching | CloudJobStatus::Running) {
    confirm_job(id)?; // /dispatch confirm <id>
}

Prevention

When it happens

Trigger: Calling run_confirmed_job (directly or via spawn_confirmed_runner) with a job id whose stored CloudJobStatus is not Launching or Running — typically a job that was never confirmed, or one already completed/cancelled.

Common situations: Forgetting `/dispatch confirm <id>` after creating a job, retrying a job that already finished, or attempting to re-run a cancelled job.

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 Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/69082724b28ab005. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/dispatch_runner.rs:83

/// Run one confirmed job end to end.
///
/// Requires the job to be `launching` (or `running`, for a resumed runner):
/// a `proposed` job is refused — confirmation is the caller's explicit act,
/// never the runner's. Every phase persists its transition so `/dispatch
/// show` and `/jobs` stream real progress, and a `canceled` record at any
/// checkpoint stops the run and tears the sandbox down.
pub fn run_confirmed_job(
    store: &CloudJobStore,
    id: &str,
    launcher: &dyn DaytonaLauncher,
    forge: &dyn ForgePr,
) -> Result<CloudJob> {
    let mut job = store.load(id)?;
    if !matches!(
        job.status,
        CloudJobStatus::Launching | CloudJobStatus::Running
    ) {
        bail!(
            "Cloud job {id} is {} and cannot be run; confirm it first with `/dispatch confirm {id}`.",
            status_word(job.status)
        );
    }
    match drive(store, &mut job, launcher, forge) {
        Ok(()) => store.load(id),
        Err(error) => {
            let message = sanitize_error(&error.to_string());
            // Re-load before writing any failure: a job the user canceled
            // stays canceled — a later run error must not overwrite the
            // user's terminal word with `failed`. The error is appended to
            // the note instead. And if a cancel lands inside the load→save
            // span of the failure write, the disk record (already
            // `canceled`, with the user's note) wins and is left alone.
            let current = match store.load(id) {
                Ok(mut record) if record.status == CloudJobStatus::Canceled => {
                    record.finished_unix =
                        Some(record.finished_unix.unwrap_or_else(unix_timestamp));

View on GitHub (pinned to 73e0f67d83)