affaan-m/ECC · error · ClaimError
obligation completion failed
Error message
obligation completion failed
What it means
_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.
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.
Example fix
// before: finishing a stale claim after revocation
claims.complete(db, stale_token, coordinate, now=ts) # obligation no longer 'approved'
// after
status = db.execute("SELECT status FROM obligations WHERE id=?", (obligation_id,)).fetchone()['status']
if status == 'approved':
claims.complete(db, token, coordinate, now=ts)
else:
claims.mark_unknown(db, token, now=ts) # or handle revocation path Defensive patterns
Strategy: validation
Validate before calling
ob = db.execute("SELECT status FROM obligations WHERE id=?", (obligation_id,)).fetchone()
if ob is None or ob['status'] != 'approved':
raise ValueError("obligation no longer approved; do not complete the claim") Try / catch
try:
claims.complete(db, token, coordinate, now=ts)
except claims.ClaimError:
claims.mark_unknown(db, token, now=ts) # dispatched but no longer completable Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- dispatch transition lost
- Invalid session state transition
- a current bound approved draft is required
- a top-level committed transaction is required
- an active harness configuration already exists
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/aad9ee3faa54390c.
Report an issue: GitHub.
Appendix: source
Thrown at skills/operator-approval-loop/references/approval_claims.py:154
(row['obligation_id'], row['decision_id'])).fetchone()
if row['state'] == 'delivered':
if receipt is None or receipt['coordinate'] != coordinate or receipt['kind'] != 'draft_sent':
raise ClaimError('completion contradicts the existing receipt')
return False
expected_state = 'dispatching' if evidence is None else 'unknown'
if row['state'] != expected_state:
raise ClaimError('completion requires the correct dispatch/reconciliation state')
_snapshot(db, row['obligation_id'], row['decision_id'])
db.execute('''INSERT INTO obligation_deliveries
(obligation_id,decision_id,kind,coordinate,delivered_ts) VALUES (?,?,'draft_sent',?,?)''',
(row['obligation_id'], row['decision_id'], coordinate, now))
db.execute('''UPDATE obligation_delivery_claims
SET state='delivered',updated_ts=?,reconciliation_evidence=? WHERE token=?''',
(now, evidence, token))
changed = db.execute("UPDATE obligations SET status='sent' WHERE id=? AND status='approved'",
(row['obligation_id'],)).rowcount
if changed != 1:
raise ClaimError('obligation completion failed')
return True
def complete(db, token, coordinate, *, now):
"""Atomically record a confirmed result; identical duplicate completion is a no-op."""
return _finish(db, token, coordinate, now, None)
def reconcile(db, token, coordinate, evidence, *, now):
"""Trusted caller supplies verified outcome evidence; this does not verify it.
No cancellation/retry of unknown claims is provided: a paused original
executor could still act. Operator authentication is outside this reference.
"""
if not isinstance(evidence, str) or not evidence.strip():
raise ClaimError('trusted reconciliation evidence is required')
return _finish(db, token, coordinate, now, evidence)
View on GitHub (pinned to 8321021c54)