zeroclaw-labs/zeroclaw · error · anyhow::Error
goal task {task_id} is terminal or missing
Error message
goal task {task_id} is terminal or missing What it means
pause_goal_task runs in one transaction: it first validates the goal row (ensure_goal_task_row), then sets the canonical status to Paused via update_task_status_record, which only matches existing, non-terminal tasks. Zero rows means the task is already terminal (done/cancelled/failed) or was deleted; the transaction rolls back, so the pause is atomic with the pause-record write that follows — no partial pause state.
Source
Thrown at crates/zeroclaw-runtime/src/control_plane/task_store_sqlite/goal.rs:538
async fn update_goal_pause(&self, task_id: &str, pause: Option<GoalPauseState>) -> Result<()> {
let conn = self.conn.lock();
let updated = update_goal_pause_record(&conn, task_id, pause)?;
if updated == 0 {
anyhow::bail!("goal task {task_id} has no goal extension row");
}
Ok(())
}
async fn pause_goal_task(&self, task_id: &str, pause: GoalPauseState) -> Result<()> {
let mut conn = self.conn.lock();
let tx = conn
.transaction()
.context("start pause goal task transaction")?;
ensure_goal_task_row(&tx, task_id)?;
let updated = update_task_status_record(&tx, task_id, TaskStatus::Paused, None, None)?;
if updated == 0 {
anyhow::bail!("goal task {task_id} is terminal or missing");
}
let updated = update_goal_pause_record(&tx, task_id, Some(pause))?;
if updated == 0 {
anyhow::bail!("goal task {task_id} has no goal extension row");
}
tx.commit().context("commit pause goal task transaction")?;
Ok(())
}
async fn resume_goal_task(
&self,
task_id: &str,
owner_pid: u32,
owner_boot_id: &str,
continuation_context: Option<TaskContinuationContext>,
) -> Result<()> {
let mut conn = self.conn.lock();
let tx = connView on GitHub (pinned to 88bb9c8533)
Solutions
- Refresh the task status first; if terminal, skip pausing — there is nothing to pause
- Make operator tooling idempotent: treat 'terminal or missing' as already stopped
- If the task should still be live, check for concurrent deletion and recreate it
Example fix
// before
store.pause_goal_task(id, pause).await?; // hard-fails on finished goals
// after
if let Some(t) = store.get_task(id).await? {
if t.kind == TaskKind::Goal && !t.status.is_terminal() {
store.pause_goal_task(id, pause).await?;
}
} Defensive patterns
Strategy: validation
Validate before calling
if let Some(t) = store.get_task(id).await? {
if t.kind == TaskKind::Goal && !t.status.is_terminal() {
store.pause_goal_task(id, pause).await?;
}
} Type guard
fn is_pauseable(t: &Task) -> bool {
t.kind == TaskKind::Goal && !t.status.is_terminal()
} Try / catch
match store.pause_goal_task(id, pause).await {
Err(ref e) if e.to_string().contains("is terminal or missing") => {
// already stopped (or deleted): treat as idempotent success for operator tooling
}
other => other,
} Prevention
- Refresh status immediately before lifecycle transitions — races with completion are the top cause
- Make pause/resume tooling idempotent on terminal states
- Disable UI actions for goals in terminal status
When it happens
Trigger: Pausing a goal that already completed, failed, or was cancelled; pausing an id deleted concurrently; stale ids from an earlier run or dashboard.
Common situations: Operator UI racing an agent finishing the goal; double-submitted pause commands; stale dashboards after goal completion.
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
- effective cost limit must be a non-negative finite value
- goal task {task_id} has no canonical TaskKind::Goal row and
- goal task {task_id} has no goal extension row
- purge_agent not supported by this memory backend
- rename_agent not supported by this memory backend
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/b561f92205a3873d.
Report an issue: GitHub.