{"record":{"id":"89aaab2a3928041d","repo":"affaan-m/ECC","slug":"now-must-be-a-nonnegative-integer","errorCode":null,"errorMessage":"now must be a nonnegative integer","messagePattern":"now must be a nonnegative integer","errorType":"validation","errorClass":"ClaimError","httpStatus":null,"severity":"error","filePath":"skills/operator-approval-loop/references/approval_claims.py","lineNumber":36,"sourceCode":"\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\n        WHERE obligation_id=? AND decision_id=?''', (obligation_id, decision_id)).fetchone()\n    if row is None:","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/operator-approval-loop/references/approval_claims.py#L18-L54","documentation":"All claim APIs take a now keyword used as the created/updated timestamp for claim rows, and the library requires it to be a strict int (not bool, not float, not str) that is >= 0. This guards the audit trail against malformed or misleading timestamps. Passing anything else raises ClaimError before the transaction begins.","triggerScenarios":"claim(db, oid, did, now=None); passing now='1700000000' (string), now=time.time() (float), now=True, or now=-1 to any of claim/begin_dispatch/cancel/mark_unknown/_finish.","commonSituations":"Using time.time() instead of int(time.time()); passing a datetime or ISO string; None when the caller forgot to supply a clock value; JSON round-trips turning ints into floats or strings.","solutions":["Pass an integer epoch value, e.g. now=int(time.time())","Convert floats/strings explicitly with int() before the call, verifying the value is nonnegative","If now may be absent, default it at the call site: now = now if isinstance(now, int) and now >= 0 else int(time.time())"],"exampleFix":"// before\nclaim(db, obligation_id, decision_id, now=time.time())  # float -> ClaimError\n\n// after\nclaim(db, obligation_id, decision_id, now=int(time.time()))","handlingStrategy":"type-guard","validationCode":"def valid_now(now):\n    return type(now) is int and now >= 0\n\nif not valid_now(ts): raise ValueError('now must be a nonnegative int')","typeGuard":"def is_valid_timestamp(v) -> bool:\n    return type(v) is int and v >= 0  # excludes bool and float","tryCatchPattern":"try:\n    token = claim(db, oid, did, now=ts)\nexcept ClaimError as e:\n    if 'now must be a nonnegative integer' in str(e):\n        token = claim(db, oid, did, now=int(time.time()))\n    else:\n        raise","preventionTips":["Always derive timestamps as int(time.time())","Never pass floats, bools, strings, or datetime objects directly","Centralize clock reading in one helper that returns ints","Enable type checking (mypy) so non-int arguments are caught statically"],"tags":["sqlite","validation","timestamps"],"backgroundTag":"invalid-argument-value","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"}