{"record":{"id":"51098f63546e44c4","repo":"affaan-m/ECC","slug":"a-top-level-committed-transaction-is-required","errorCode":null,"errorMessage":"a top-level committed transaction is required","messagePattern":"a top-level committed transaction is required","errorType":"validation","errorClass":"ClaimError","httpStatus":null,"severity":"error","filePath":"skills/operator-approval-loop/references/approval_claims.py","lineNumber":34,"sourceCode":"class ClaimError(Exception):\n    \"\"\"No dispatch permission or state transition was granted.\"\"\"\n\n\ndef connect(path):\n    \"\"\"Open an existing caller-selected database; never apply schema/migrations.\"\"\"\n    uri = Path(path).resolve().as_uri() + '?mode=rw'\n    db = sqlite3.connect(uri, uri=True, isolation_level=None, timeout=5)\n    db.row_factory = sqlite3.Row\n    db.execute('PRAGMA foreign_keys=ON')\n    db.execute('PRAGMA recursive_triggers=ON')\n    return db\n\n\n@contextmanager\ndef _transaction(db, now):\n    # Never return permission whose commit belongs to an outer caller transaction.\n    if db.in_transaction:\n        raise ClaimError('a top-level committed transaction is required')\n    if type(now) is not int or now < 0:\n        raise ClaimError('now must be a nonnegative integer')\n    if any(db.execute(f'PRAGMA {name}').fetchone()[0] != 1\n           for name in ('foreign_keys', 'recursive_triggers')):\n        raise ClaimError('required SQLite guards are disabled')\n    try:\n        db.execute('BEGIN IMMEDIATE')\n        yield\n        db.commit()\n    except BaseException as error:\n        db.rollback()\n        if isinstance(error, sqlite3.Error):\n            raise ClaimError('claim transaction failed; no permission granted') from error\n        raise\n\n\ndef _snapshot(db, obligation_id, decision_id):\n    row = db.execute('''SELECT * FROM approval_bound_drafts","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/operator-approval-loop/references/approval_claims.py#L16-L52","documentation":"This library refuses to run a claim operation when the SQLite connection already has an open transaction (db.in_transaction is true). It exists so that dispatch permission is never granted inside a transaction whose commit belongs to an outer caller: a nested or caller-controlled commit could leave the claim rolled back after the caller already acted on it. Every public API (claim, begin_dispatch, cancel, mark_unknown, _finish) requires a clean, top-level transaction it owns.","triggerScenarios":"Calling claim/begin_dispatch/cancel/mark_unknown/_finish while the same connection has a pending BEGIN started by your own code, by a with db: block, or by a sqlite3 implicit transaction (isolation_level not None and uncommitted DML).","commonSituations":"Wrapping a claim() call inside application-level DB transaction code; reusing a connection shared with an ORM that keeps transactions open; forgetting to commit/rollback a prior statement because isolation_level was left at the default (empty-string autocommit deferral).","solutions":["Commit or rollback the open transaction on the connection before calling any function in this module","Create the connection with isolation_level=None (autocommit) so this library's BEGIN IMMEDIATE is top-level, as its connect() helper does","Do not wrap claim()/begin_dispatch() calls inside your own transaction; perform outer work in a separate transaction or separate connection","Check db.in_transaction yourself before calling and fail fast with a clear application error"],"exampleFix":"// before\ndb = sqlite3.connect('app.db')\ndb.execute('BEGIN')\nclaim(db, obligation_id, decision_id, now=ts)  # ClaimError\n\n// after\ndb = sqlite3.connect('app.db', isolation_level=None)\ndb.commit()  # or rollback any pending work first\nclaim(db, obligation_id, decision_id, now=ts)","handlingStrategy":"validation","validationCode":"def ensure_top_level(db):\n    if db.in_transaction:\n        raise RuntimeError('commit or rollback the open transaction before calling the claims API')","typeGuard":"def is_clean_connection(db) -> bool:\n    return not getattr(db, 'in_transaction', False)","tryCatchPattern":"try:\n    token = claim(db, oid, did, now=ts)\nexcept ClaimError as e:\n    if 'top-level committed transaction' in str(e):\n        db.rollback()  # or commit, per your semantics\n        token = claim(db, oid, did, now=ts)\n    else:\n        raise","preventionTips":["Open connections with isolation_level=None (autocommit) so no implicit transactions linger","Never nest claim APIs inside your own BEGIN...COMMIT blocks","Use this module's connect() helper exclusively for claims work","Call db.commit()/db.rollback() after any ad-hoc writes before invoking the library"],"tags":["sqlite","transactions","state"],"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"}