affaan-m/ECC · error · ContractError
rule lacks immutable reference evidence
Error message
rule {rule.get('rule_id')} lacks immutable reference evidence What it means
Every evidence item must cite an immutable source via a 'reference_sha256' string of exactly 64 hex characters (a SHA-256 digest). Missing, non-string, or wrong-length digests raise this ContractError to ensure rules bind to verifiable content.
Solutions
- Set 'reference_sha256' to the 64-character lowercase hex SHA-256 of the referenced file, e.g. hashlib.sha256(data).hexdigest().
- Verify digest length with len(digest) == 64 before adding the evidence item.
- Regenerate the digest after any change to the referenced source file; stale digests mean re-hashing the current bytes.
- Ensure the digest is stored as a str, not bytes or int (decode bytes with .hex() or .decode()).
Example fix
// before
item = {"reference_sha256": hashlib.md5(data).hexdigest()} # 32 chars
// after
item = {"reference_sha256": hashlib.sha256(data).hexdigest()} # 64 chars Defensive patterns
Strategy: validation
Validate before calling
import hashlib, re
SHA256_RE = re.compile(r"^[0-9a-f]{64}$")
def digest_ok(item):
d = item.get("reference_sha256")
return isinstance(d, str) and SHA256_RE.match(d) is not None
bad = [i for i in evidence_items if not digest_ok(i)] # fix before validating Type guard
def has_valid_sha256(item: dict) -> bool:
d = item.get("reference_sha256")
return isinstance(d, str) and len(d) == 64 and all(c in "0123456789abcdef" for c in d) Try / catch
try:
validate_provenance(payload)
except ContractError as e:
if "immutable reference evidence" in str(e):
rehash_all_evidence(payload) # recompute sha256 hex digests
validate_provenance(payload)
else:
raise Prevention
- Always compute digests with hashlib.sha256(data).hexdigest() — never md5 or base64.
- Decode bytes digests with .hex() so the stored value is a str.
- Re-hash evidence sources after any modification; keep hashing in one shared utility.
When it happens
Trigger: An evidence item with no 'reference_sha256' key, digest = 123 (int), digest = 'abc123' (not 64 chars), or digest = 63/65-character string passed to validate_provenance.
Common situations: Computing the digest with a truncated/hex-vs-base64 mismatch; storing the hash as bytes instead of hex string; forgetting to compute the hash at all for a new evidence source; using an md5 (32-char) digest instead of SHA-256.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- anchor evidence source duration is not bound to its receipt…
- artifact has invalid reference SHA-256
- receipt source SHA-256 changed after generation
- a generated candidate cannot claim original-source identity
- approved hash must be lowercase SHA-256 hexadecimal
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/ff6002e7f61d428c.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:270
for event in events:
placement = event.get("placement")
if not isinstance(placement, dict) or not {"safe_area", "max_coverage", "occlusion_policy"}.issubset(placement):
raise ContractError(f"effect {event.get('effect')} lacks placement constraints")
def validate_provenance(payload: dict[str, Any]) -> None:
"""Require every declared rule to cite immutable, timestamped evidence."""
rules = payload.get("rules")
if not isinstance(rules, list) or not rules:
raise ContractError("provenance must contain derived rules")
for rule in rules:
evidence = rule.get("evidence")
if not isinstance(evidence, list) or not evidence:
raise ContractError(f"rule {rule.get('rule_id')} lacks reference evidence")
for item in evidence:
digest = item.get("reference_sha256")
if not isinstance(digest, str) or len(digest) != 64:
raise ContractError(f"rule {rule.get('rule_id')} lacks immutable reference evidence")
times = item.get("times")
if not isinstance(times, list) or not times:
raise ContractError(f"rule {rule.get('rule_id')} lacks time evidence")
source_duration = item.get("source_duration")
for time in times:
_validate_media_time(
time, source_duration, label=f"rule {rule.get('rule_id')} time evidence"
)
def validate_manifests(manifests_dir: str | Path) -> None:
"""Require image, video, and 3D-asset dry-run request manifests."""
manifests_dir = Path(manifests_dir)
found = {path.stem for path in manifests_dir.glob("*.json")} if manifests_dir.is_dir() else set()
missing = _REQUIRED_MODALITIES - found
if missing:
raise ContractError(f"missing modality manifests: {sorted(missing)}")
for modality in _REQUIRED_MODALITIES:View on GitHub (pinned to 8321021c54)