Hmbown/CodeWhale · error
lane ` ` was stopped before tmux dry-run start completed
Error message
lane `{}` was stopped before tmux dry-run start completed What it means
During a tmux dry-run start (the `CODEWHALE_LANE_TMUX_DRY_RUN` test hook), the runtime records the session without spawning a process and then atomically transitions the lane record to Running via `mark_running_if_pending`. If the pending→running transition fails, the lane was concurrently stopped (or is otherwise no longer pending), and the runtime bails rather than overwriting that stop. This protects the invariant that a stop wins over an in-flight start.
Solutions
- Retry the start once the concurrent stop has completed — the lane state is consistent, the start just lost the race.
- Serialize start/stop for a given lane (per-lane lock or queue) in your orchestration layer so starts and stops cannot interleave.
- Check the lane record's status and log to confirm who stopped it; do not force the record back to Running.
- If seen without any concurrent stop, inspect the registry record for corruption — status should have been Pending.
Defensive patterns
Strategy: try-catch
Prevention
- Serialize start/stop operations per lane id.
- Avoid issuing stop and start concurrently for the same lane in scripts/tests.
- Check the lane log to confirm the concurrent stop before retrying.
When it happens
Trigger: Calling `start` with the dry-run env var set while another thread/process stops the lane between the pending record creation and the `mark_running_if_pending` call — a genuine race — or a corrupted record whose status is not Pending at that point.
Common situations: Test/CI harnesses driving dry-run starts concurrently with stop calls; a user issuing `lane stop` while `lane start` is in flight; duplicate start requests for the same lane id where one path already stopped it.
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
- lane ` ` was stopped before tmux launch
- parent turn is already settling foreground work as
- session was deleted
- tmux session remains active after kill-session ( )
- Agent not found
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/327cac93b0dd1d8f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/lane/src/runtime.rs:729
"workflow": record.workflow,
"fleet": record.fleet,
"issue": record.issue,
"dry_run": dry_run,
}),
)?;
if dry_run {
append_log_event(
&record.log_path,
serde_json::json!({
"type": "lane_log",
"message": "tmux dry-run: session recorded without spawning process",
"command": spec.command,
"cwd": cwd.as_ref().map(|p| p.display().to_string()),
}),
)?;
if !registry.mark_running_if_pending(record)? {
bail!(
"lane `{}` was stopped before tmux dry-run start completed",
record.id
);
}
return Ok(());
}
let log_proxy = spec
.log_proxy
.as_deref()
.context("tmux runtime requires a lane log proxy executable")?;
// Detached session: child output remains an operator journal only.
// Terminal control state goes through a separate bounded, atomically
// renamed receipt so child stdout cannot forge Lane completion.
let receipt_path = lane_exit_receipt_path(&record.log_path);
let receipt_tmp_path = lane_exit_receipt_tmp_path(&record.log_path);
let environment_path = lane_environment_path(&record.log_path);View on GitHub (pinned to 73e0f67d83)