Hmbown/CodeWhale · error
exec turn ended with status
Error message
exec turn ended with status {status} What it means
After a CLI exec turn finishes, run_exec_agent checks the summary status; statuses of "failed", "canceled", or "interrupted" (or a missing status, reported as "unknown") are not successful completions, so the function bails with this error. It distinguishes a turn that ran but ended abnormally from one that threw an exception (error 893).
Solutions
- Check the status word in the message: interrupted/canceled usually means a signal or cancellation — rerun without interrupting.
- For "failed", inspect the session summary/logs for the failing step or tool output.
- For "unknown", verify the session summary was written by a compatible Codewhale version and is not truncated.
- Adjust the orchestrator to treat this exit as a turn-level failure and retry with backoff if appropriate.
Example fix
// before codewhale exec "long task" & # killed by timeout => status interrupted // after timeout 30m codewhale exec "long task" || echo "turn ended with $?, inspect summary"
Defensive patterns
Strategy: retry
Try / catch
let out = Command::new("codewhale").args(["exec", prompt]).output()?;
if !out.status.success() {
let msg = String::from_utf8_lossy(&out.stderr);
if msg.contains("ended with status interrupted") || msg.contains("ended with status canceled") {
// reschedule rather than treating as a hard failure
}
} Prevention
- Protect long exec turns from SIGINT/SIGTERM in orchestrators, or handle resumption.
- Use timeouts generous enough for the turn, and prefer explicit cancel APIs over signals.
- Inspect the session summary status after each run and classify interrupted vs failed before retrying.
When it happens
Trigger: Calling `codewhale exec` where the completed turn's summary reports status failed/canceled/interrupted — e.g. the model turn was cancelled by a signal, hit a failure classification, or the summary lacked a recognized status.
Common situations: User or orchestrator sent SIGINT/SIGTERM mid-turn; the turn hit the failure path recorded in the session summary; a bug or old session format produced a summary without a status string (shows as "unknown").
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
- exec turn failed
- Command failed with exit code
- dsh exited with status
- must be placed before `exec`. Use: codewhale exec
- 127
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/37a3f183a3aa4d85.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/exec_agent.rs:1452
// after all in-session retries are exhausted) from genuine task
// failures so supervisors and bench harnesses can tell them apart at
// the process level without parsing the stream. Genuine failures
// keep the historical `bail!` → exit 1 path.
let exit_code = exec_failure_exit_code(summary.error_category.as_deref());
if exit_code != 1 {
eprintln!("Error: exec turn failed: {error}");
let _ = io::stdout().flush();
std::process::exit(exit_code);
}
bail!("exec turn failed: {error}");
}
if matches!(
summary.status.as_deref(),
Some("failed" | "canceled" | "interrupted")
) {
let status = summary.status.as_deref().unwrap_or("unknown");
bail!("exec turn ended with status {status}");
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::{ExecAgentEvents, exec_automation_services, exec_disallowed_tools};
use crate::core::engine::mock_engine_handle;
use crate::core::engine::tool_catalog::REQUEST_USER_INPUT_NAME;
use crate::core::events::{Event, TurnOutcomeStatus};
use crate::core::ops::{Op, SubAgentSettlement};
use codewhale_models::Usage;
use std::time::{Duration, Instant};
fn completed_parent(input_tokens: u32) -> Event {
let usage = Usage {
input_tokens,View on GitHub (pinned to 73e0f67d83)