{"record":{"id":"28129b4a86845900","repo":"zeroclaw-labs/zeroclaw","slug":"goal-task-task-id-has-no-goal-extension-row","errorCode":null,"errorMessage":"goal task {task_id} has no goal extension row","messagePattern":"goal task (.+?) has no goal extension row","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/control_plane/task_store_sqlite/goal.rs","lineNumber":492,"sourceCode":"            params![task_id],\n            row_to_goal_task,\n        )\n        .optional()\n        .context(\"get goal task\")\n    }\n\n    async fn update_goal_objective(&self, task_id: &str, objective: &str) -> Result<()> {\n        let conn = self.conn.lock();\n        let updated = conn\n            .execute(\n                \"UPDATE goal_tasks\n                    SET objective = ?1\n                  WHERE task_id = ?2\",\n                params![objective, task_id],\n            )\n            .context(\"update goal objective\")?;\n        if updated == 0 {\n            anyhow::bail!(\"goal task {task_id} has no goal extension row\");\n        }\n        Ok(())\n    }\n\n    async fn update_goal_limits(\n        &self,\n        task_id: &str,\n        token_limit: Option<u64>,\n        cost_limit_usd: Option<f64>,\n    ) -> Result<()> {\n        let (effective_token_limit, effective_cost_limit_usd) =\n            goal_limits_to_db(token_limit, cost_limit_usd)?;\n        let conn = self.conn.lock();\n        let updated = conn\n            .execute(\n                \"UPDATE goal_tasks\n                SET effective_token_limit = ?1,\n                    effective_cost_limit_usd = ?2","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/control_plane/task_store_sqlite/goal.rs#L474-L510","documentation":"update_goal_objective runs an UPDATE against the goal extension table and checks rows affected. Zero affected rows means no goal extension row exists for that task id, so the objective change is refused instead of silently no-opping — the caller learns the goal is structurally incomplete.","triggerScenarios":"update_goal_objective on a task whose canonical row exists but whose goal extension row does not: partial creation, manual INSERT of only the canonical row, corrupted store, or concurrent extension deletion.","commonSituations":"Stores upgraded across schema versions where extension inserts were missed; test fixtures inserting canonical rows directly; admin scripts mutating the SQLite file.","solutions":["Confirm the goal was created via the goal creation API (it writes both rows atomically)","Recreate the goal if the extension row is missing","Fix fixtures/scripts that insert task rows without the paired extension"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if !goal_extension_exists(&store, id).await? {\n    anyhow::bail!(\"goal {id} lacks an extension row; recreate it before updating\");\n}\nstore.update_goal_objective(id, objective).await?;","typeGuard":null,"tryCatchPattern":"match store.update_goal_objective(id, obj).await {\n    Err(ref e) if e.to_string().contains(\"has no goal extension row\") => {\n        // treat as structurally-missing entity: recreate the goal, then reapply the objective\n    }\n    other => other,\n}","preventionTips":["Create goals exclusively via the creation API","Keep test fixtures from inserting half a goal","After schema migrations, verify canonical and extension row counts match"],"tags":["goal-store","update-noop","missing-row","sqlite"],"backgroundTag":"entity-not-found","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}