{"record":{"id":"b561f92205a3873d","repo":"zeroclaw-labs/zeroclaw","slug":"goal-task-task-id-is-terminal-or-missing","errorCode":null,"errorMessage":"goal task {task_id} is terminal or missing","messagePattern":"goal task (.+?) is terminal or missing","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/control_plane/task_store_sqlite/goal.rs","lineNumber":538,"sourceCode":"\n    async fn update_goal_pause(&self, task_id: &str, pause: Option<GoalPauseState>) -> Result<()> {\n        let conn = self.conn.lock();\n        let updated = update_goal_pause_record(&conn, task_id, pause)?;\n        if updated == 0 {\n            anyhow::bail!(\"goal task {task_id} has no goal extension row\");\n        }\n        Ok(())\n    }\n\n    async fn pause_goal_task(&self, task_id: &str, pause: GoalPauseState) -> Result<()> {\n        let mut conn = self.conn.lock();\n        let tx = conn\n            .transaction()\n            .context(\"start pause goal task transaction\")?;\n        ensure_goal_task_row(&tx, task_id)?;\n        let updated = update_task_status_record(&tx, task_id, TaskStatus::Paused, None, None)?;\n        if updated == 0 {\n            anyhow::bail!(\"goal task {task_id} is terminal or missing\");\n        }\n        let updated = update_goal_pause_record(&tx, task_id, Some(pause))?;\n        if updated == 0 {\n            anyhow::bail!(\"goal task {task_id} has no goal extension row\");\n        }\n        tx.commit().context(\"commit pause goal task transaction\")?;\n        Ok(())\n    }\n\n    async fn resume_goal_task(\n        &self,\n        task_id: &str,\n        owner_pid: u32,\n        owner_boot_id: &str,\n        continuation_context: Option<TaskContinuationContext>,\n    ) -> Result<()> {\n        let mut conn = self.conn.lock();\n        let tx = conn","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/control_plane/task_store_sqlite/goal.rs#L520-L556","documentation":"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.","triggerScenarios":"Pausing a goal that already completed, failed, or was cancelled; pausing an id deleted concurrently; stale ids from an earlier run or dashboard.","commonSituations":"Operator UI racing an agent finishing the goal; double-submitted pause commands; stale dashboards after goal completion.","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"],"exampleFix":"// before\nstore.pause_goal_task(id, pause).await?; // hard-fails on finished goals\n\n// after\nif let Some(t) = store.get_task(id).await? {\n    if t.kind == TaskKind::Goal && !t.status.is_terminal() {\n        store.pause_goal_task(id, pause).await?;\n    }\n}","handlingStrategy":"validation","validationCode":"if let Some(t) = store.get_task(id).await? {\n    if t.kind == TaskKind::Goal && !t.status.is_terminal() {\n        store.pause_goal_task(id, pause).await?;\n    }\n}","typeGuard":"fn is_pauseable(t: &Task) -> bool {\n    t.kind == TaskKind::Goal && !t.status.is_terminal()\n}","tryCatchPattern":"match store.pause_goal_task(id, pause).await {\n    Err(ref e) if e.to_string().contains(\"is terminal or missing\") => {\n        // already stopped (or deleted): treat as idempotent success for operator tooling\n    }\n    other => other,\n}","preventionTips":["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"],"tags":["goal-store","state-transition","terminal-state","pause","sqlite"],"backgroundTag":"invalid-state-transition","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}