{"record":{"id":"2e3f3e3627afecab","repo":"affaan-m/ECC","slug":"a-claim-token-is-required","errorCode":null,"errorMessage":"a claim token is required","messagePattern":"a claim token is required","errorType":"validation","errorClass":"ClaimError","httpStatus":null,"severity":"error","filePath":"skills/operator-approval-loop/references/approval_claims.py","lineNumber":71,"sourceCode":"        WHERE obligation_id=? AND decision_id=?''', (obligation_id, decision_id)).fetchone()\n    if row is None:\n        raise ClaimError('a current bound approved draft is required')\n    try:\n        digest = hashlib.sha256(row['draft_text'].encode('utf-8')).hexdigest()\n    except (AttributeError, UnicodeError) as error:\n        raise ClaimError('approved text must be valid UTF-8 text') from error\n    stored_digest = row['draft_sha256']\n    if (not isinstance(stored_digest, str) or len(stored_digest) != 64\n            or any(character not in '0123456789abcdef' for character in stored_digest)):\n        raise ClaimError('approved hash must be lowercase SHA-256 hexadecimal')\n    if not secrets.compare_digest(digest, stored_digest):\n        raise ClaimError('approved text hash does not match')\n    return dict(row)\n\n\ndef _claim_row(db, token):\n    if not isinstance(token, str) or not token:\n        raise ClaimError('a claim token is required')\n    row = db.execute('SELECT * FROM obligation_delivery_claims WHERE token=?', (token,)).fetchone()\n    if row is None:\n        raise ClaimError('unknown claim token')\n    return row\n\n\ndef claim(db, obligation_id, decision_id, *, now):\n    \"\"\"Reserve one already-authorized decision; return only a random claim token.\"\"\"\n    with _transaction(db, now):\n        _snapshot(db, obligation_id, decision_id)\n        token = secrets.token_hex(32)\n        db.execute('''INSERT INTO obligation_delivery_claims\n            (obligation_id,decision_id,token,state,created_ts,updated_ts)\n            VALUES (?,?,?,'claimed',?,?)''', (obligation_id, decision_id, token, now, now))\n    return token\n\n\ndef begin_dispatch(db, token, *, now):","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/operator-approval-loop/references/approval_claims.py#L53-L89","documentation":"The token parameter identifying a delivery claim must be a non-empty string; begin_dispatch, cancel, mark_unknown, and _finish all validate this before any database lookup. It prevents nonsense or None values from reaching parameterized queries and keeps the claim-identity contract explicit.","triggerScenarios":"begin_dispatch(db, None, now=ts); cancel(db, '', now=ts); passing a bytes token (b'abc'), an int ID, or a token variable that was never assigned because claim() failed earlier.","commonSituations":"Losing the return value of claim() and passing the obligation ID instead; deserializing tokens from JSON/config where they became None; storing tokens in a column or config that stripped/emptied them.","solutions":["Pass the exact token string returned by claim(): token = claim(...); begin_dispatch(db, token, now=ts)","Guard before calling: if not token or not isinstance(token, str): handle the missing-token case in your code","Persist the token durably (database/job record) before dispatching so restarts can recover it","If claim() failed, do not fabricate a token; rerun the approval/claim flow"],"exampleFix":"// before\ntoken = None  # claim() result lost\nbegin_dispatch(db, token, now=ts)  # ClaimError\n\n// after\ntoken = claim(db, oid, did, now=ts)\nassert isinstance(token, str) and token\nbegin_dispatch(db, token, now=ts)","handlingStrategy":"type-guard","validationCode":"def require_token(token):\n    if not isinstance(token, str) or not token:\n        raise ValueError('a non-empty claim token string is required')","typeGuard":"def is_claim_token(v) -> bool:\n    return isinstance(v, str) and len(v) > 0","tryCatchPattern":"try:\n    payload = begin_dispatch(db, token, now=ts)\nexcept ClaimError as e:\n    if 'a claim token is required' in str(e):\n        raise AppError('claim token missing; recover it from your job record or re-claim') from e\n    raise","preventionTips":["Always capture the return value of claim() and persist it before dispatching","Validate tokens at system boundaries (job queues, config) where they may become None","Never substitute obligation IDs or decision IDs for tokens","Type-annotate token parameters as str and run mypy"],"tags":["validation","missing-argument","tokens"],"backgroundTag":"missing-required-argument","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"}