Hmbown/CodeWhale · error · anyhow::Error

lane `{}` was stopped before tmux launch

Error message

lane `{}` was stopped before tmux launch

What it means

mark_running_if_pending_with returns Ok(false) when the lane stopped concurrently while the tmux launch was in flight. The runtime removes the private environment file, adopts the stopped timestamp, cleans up the lane's worktree, and bails — leaving a consistent Stopped record instead of resurrecting a Running lane the user asked to stop.

Source

Thrown at crates/lane/src/runtime.rs:802

                }
                spawned.set(true);
                Ok(())
            },
            || {
                stop_tmux_session(&socket, &session)?;
                rolled_back.set(true);
                Ok(())
            },
        ) {
            Ok(true) => {}
            Ok(false) => {
                if let Some(path) = environment_path.as_deref() {
                    remove_file_if_present(path)?;
                }
                let mut stopped_record = proposed_record;
                stopped_record.stopped_at = record.stopped_at.clone();
                self.cleanup_worktree(&stopped_record)?;
                bail!("lane `{}` was stopped before tmux launch", record.id);
            }
            Err(error) => {
                if let Some(path) = environment_path.as_deref() {
                    let _ = remove_file_if_present(path);
                }
                if !spawned.get() || rolled_back.get() {
                    let _ = registry.mark_terminal_if_active(record, LaneStatus::Failed)?;
                    let mut failed_record = proposed_record;
                    failed_record.stopped_at = record.stopped_at.clone();
                    self.cleanup_worktree(&failed_record)?;
                }
                return Err(error);
            }
        }
        Ok(())
    }

    fn attach_command(&self, record: &LaneRecord) -> Option<String> {

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Accept the outcome: refetch the lane record — it is Stopped with the worktree cleaned up; do not retry the start unless intended
  2. Guard against the race by waiting for start to return before issuing stop
  3. Use fences/registry transitions to serialize concurrent lifecycle operations on one lane
Defensive patterns

Strategy: try-catch

Try / catch

match runtime.start(&registry, &mut record, &spec) {
    Ok(()) => { /* running */ }
    Err(err) if err.to_string().contains("stopped before tmux launch") => {
        // Concurrent stop won: environment file removed and worktree cleaned.
        let fresh = registry.get(&record.id)?.expect("lane exists");
        assert_eq!(fresh.status, LaneStatus::Stopped);
        return Ok(()); // user asked to stop; that is the achieved state
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: A stop request (or fenced terminal transition) landing between the start's beginning and its Pending->Running commit for the same lane — e.g. a UI stop button clicked while start is still spawning tmux, or automation issuing stop immediately after start.

Common situations: Interactive lane managers where users cancel during launch; scripts that start and immediately stop on timeout; concurrent processes operating one lane id without coordination.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/c75c0e2bccd4c4db. Report an issue: GitHub.