{"record":{"id":"aad9ee3faa54390c","repo":"affaan-m/ECC","slug":"obligation-completion-failed","errorCode":null,"errorMessage":"obligation completion failed","messagePattern":"obligation completion failed","errorType":"exception","errorClass":"ClaimError","httpStatus":null,"severity":"error","filePath":"skills/operator-approval-loop/references/approval_claims.py","lineNumber":154,"sourceCode":"                             (row['obligation_id'], row['decision_id'])).fetchone()\n        if row['state'] == 'delivered':\n            if receipt is None or receipt['coordinate'] != coordinate or receipt['kind'] != 'draft_sent':\n                raise ClaimError('completion contradicts the existing receipt')\n            return False\n        expected_state = 'dispatching' if evidence is None else 'unknown'\n        if row['state'] != expected_state:\n            raise ClaimError('completion requires the correct dispatch/reconciliation state')\n        _snapshot(db, row['obligation_id'], row['decision_id'])\n        db.execute('''INSERT INTO obligation_deliveries\n            (obligation_id,decision_id,kind,coordinate,delivered_ts) VALUES (?,?,'draft_sent',?,?)''',\n                   (row['obligation_id'], row['decision_id'], coordinate, now))\n        db.execute('''UPDATE obligation_delivery_claims\n            SET state='delivered',updated_ts=?,reconciliation_evidence=? WHERE token=?''',\n                   (now, evidence, token))\n        changed = db.execute(\"UPDATE obligations SET status='sent' WHERE id=? AND status='approved'\",\n                             (row['obligation_id'],)).rowcount\n        if changed != 1:\n            raise ClaimError('obligation completion failed')\n    return True\n\n\ndef complete(db, token, coordinate, *, now):\n    \"\"\"Atomically record a confirmed result; identical duplicate completion is a no-op.\"\"\"\n    return _finish(db, token, coordinate, now, None)\n\n\ndef reconcile(db, token, coordinate, evidence, *, now):\n    \"\"\"Trusted caller supplies verified outcome evidence; this does not verify it.\n\n    No cancellation/retry of unknown claims is provided: a paused original\n    executor could still act. Operator authentication is outside this reference.\n    \"\"\"\n    if not isinstance(evidence, str) or not evidence.strip():\n        raise ClaimError('trusted reconciliation evidence is required')\n    return _finish(db, token, coordinate, now, evidence)\n","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/operator-approval-loop/references/approval_claims.py#L136-L172","documentation":"_finish raises this when the final guarded UPDATE obligations SET status='sent' WHERE id=? AND status='approved' affected a rowcount other than 1. The obligation is no longer in 'approved' status at completion time (or was deleted), so recording the delivery would contradict the obligation lifecycle. The whole transaction is rolled back.","triggerScenarios":"The obligation's status was changed from 'approved' (e.g. to a revoked/expired/withdrawn state) between claim() and complete()/reconcile(); the obligation row was deleted by another writer; another module updated status concurrently.","commonSituations":"A long-lived claim outliving an approval revocation; an admin tool or other workflow mutating the obligations table outside this module; a restarted pipeline finishing a stale claim days later.","solutions":["Re-verify the obligation is still 'approved' before finishing: SELECT status FROM obligations WHERE id=?; if not, abandon the claim (mark_unknown for a dispatched attempt) rather than completing.","Shorten the window between claim() and completion; complete promptly after dispatch.","Ensure all writers to the obligations table go through the guarded, status-checked updates this module uses.","If the row was deleted, investigate the deleting writer; the claim cannot be finished against a missing obligation."],"exampleFix":"// before: finishing a stale claim after revocation\nclaims.complete(db, stale_token, coordinate, now=ts)  # obligation no longer 'approved'\n\n// after\nstatus = db.execute(\"SELECT status FROM obligations WHERE id=?\", (obligation_id,)).fetchone()['status']\nif status == 'approved':\n    claims.complete(db, token, coordinate, now=ts)\nelse:\n    claims.mark_unknown(db, token, now=ts)  # or handle revocation path","handlingStrategy":"validation","validationCode":"ob = db.execute(\"SELECT status FROM obligations WHERE id=?\", (obligation_id,)).fetchone()\nif ob is None or ob['status'] != 'approved':\n    raise ValueError(\"obligation no longer approved; do not complete the claim\")","typeGuard":null,"tryCatchPattern":"try:\n    claims.complete(db, token, coordinate, now=ts)\nexcept claims.ClaimError:\n    claims.mark_unknown(db, token, now=ts)  # dispatched but no longer completable","preventionTips":["Complete claims promptly; don't let them outlive approval state","Route all obligation status changes through guarded updates","Re-check obligation status after long pauses before finishing"],"tags":["state-machine","lifecycle","sqlite","stale-data"],"backgroundTag":"invalid-state-transition","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}