affaan-m/ECC · error · ClaimError
required SQLite guards are disabled
Error message
required SQLite guards are disabled
What it means
The library verifies at transaction start that the PRAGMAs foreign_keys and recursive_triggers are both enabled (return 1) on the connection, because its integrity model depends on enforced foreign keys and recursive trigger behavior. If either is off it refuses to proceed, since a claim could otherwise be committed against inconsistent or orphaned rows.
Solutions
- Create the connection with this module's connect(path) helper, which enables both PRAGMAs
- Run db.execute('PRAGMA foreign_keys=ON') and db.execute('PRAGMA recursive_triggers=ON') on the connection before calling the API
- Check current values with PRAGMA foreign_keys; PRAGMA recursive_triggers and re-enable if 0
- Avoid code paths that toggle foreign_keys off on the shared connection; use a separate connection for migrations
Example fix
// before
db = sqlite3.connect('app.db') # foreign_keys defaults to OFF
claim(db, oid, did, now=ts) # ClaimError
// after
db = sqlite3.connect('app.db', isolation_level=None)
db.execute('PRAGMA foreign_keys=ON')
db.execute('PRAGMA recursive_triggers=ON')
claim(db, oid, did, now=ts) Defensive patterns
Strategy: validation
Validate before calling
def ensure_sqlite_guards(db):
for name in ('foreign_keys', 'recursive_triggers'):
if db.execute(f'PRAGMA {name}').fetchone()[0] != 1:
db.execute(f'PRAGMA {name}=ON') Try / catch
try:
token = claim(db, oid, did, now=ts)
except ClaimError as e:
if 'required SQLite guards are disabled' in str(e):
db.execute('PRAGMA foreign_keys=ON')
db.execute('PRAGMA recursive_triggers=ON')
token = claim(db, oid, did, now=ts)
else:
raise Prevention
- Always construct connections via the module's connect() helper
- Re-check PRAGMAs after any code that may have toggled them (migrations, backups)
- Never share a connection with bulk-load/migration tooling that disables foreign_keys
- Add a startup assertion that both PRAGMAs return 1
When it happens
Trigger: Using a connection created outside this module's connect() helper where foreign_keys was never enabled (the SQLite default is OFF per connection), or where something later executed PRAGMA foreign_keys=OFF (e.g. during a migration or backup routine) and never re-enabled it.
Common situations: Sharing a connection with migration code that disables foreign keys for bulk loading; creating sqlite3.connect() directly instead of via connect(); switching to another library/tool that resets PRAGMAs on the same connection.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- a current bound approved draft is required
- a top-level committed transaction is required
- Announcements discussion category is required
- approved hash must be lowercase SHA-256 hexadecimal
- approved text must be valid UTF-8 text
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/480150f7ba9f6013.
Report an issue: GitHub.
Appendix: source
Thrown at skills/operator-approval-loop/references/approval_claims.py:39
"""Open an existing caller-selected database; never apply schema/migrations."""
uri = Path(path).resolve().as_uri() + '?mode=rw'
db = sqlite3.connect(uri, uri=True, isolation_level=None, timeout=5)
db.row_factory = sqlite3.Row
db.execute('PRAGMA foreign_keys=ON')
db.execute('PRAGMA recursive_triggers=ON')
return db
@contextmanager
def _transaction(db, now):
# Never return permission whose commit belongs to an outer caller transaction.
if db.in_transaction:
raise ClaimError('a top-level committed transaction is required')
if type(now) is not int or now < 0:
raise ClaimError('now must be a nonnegative integer')
if any(db.execute(f'PRAGMA {name}').fetchone()[0] != 1
for name in ('foreign_keys', 'recursive_triggers')):
raise ClaimError('required SQLite guards are disabled')
try:
db.execute('BEGIN IMMEDIATE')
yield
db.commit()
except BaseException as error:
db.rollback()
if isinstance(error, sqlite3.Error):
raise ClaimError('claim transaction failed; no permission granted') from error
raise
def _snapshot(db, obligation_id, decision_id):
row = db.execute('''SELECT * FROM approval_bound_drafts
WHERE obligation_id=? AND decision_id=?''', (obligation_id, decision_id)).fetchone()
if row is None:
raise ClaimError('a current bound approved draft is required')
try:
digest = hashlib.sha256(row['draft_text'].encode('utf-8')).hexdigest()View on GitHub (pinned to 8321021c54)