affaan-m/ECC · error · ContractError
provenance must contain derived rules
Error message
provenance must contain derived rules
What it means
validate_provenance enforces that a Tasteforge provenance payload declares at least one derived rule. If the 'rules' key is absent, not a list, or an empty list, this ContractError is raised to guarantee every generated artifact carries non-empty provenance rules.
Solutions
- Add a non-empty 'rules' list to the provenance payload, each entry with 'rule_id' and non-empty 'evidence'.
- Verify the producer that derives rules actually ran and wrote its output (check for upstream failures that yield zero rules).
- Confirm the correct provenance file is being loaded (not a stub/placeholder file).
- If zero rules is legitimately valid for your use case, bypass/extend the validator rather than passing an empty payload.
Example fix
// before
payload = {"rules": []}
// after
payload = {"rules": [{"rule_id": "r1", "evidence": [{"reference_sha256": "<64-hex>", "times": [0.0, 1.5], "source_duration": 10.0}]}]} Defensive patterns
Strategy: validation
Validate before calling
def has_rules(payload):
rules = payload.get("rules")
return isinstance(rules, list) and len(rules) > 0
if not has_rules(payload):
raise ValueError("provenance payload must declare a non-empty 'rules' list before validation") Type guard
def is_provenance_payload(p: object) -> bool:
return isinstance(p, dict) and isinstance(p.get("rules"), list) and len(p["rules"]) > 0 Try / catch
try:
validate_provenance(payload)
except ContractError as e:
logger.error("provenance rejected: %s", e)
payload = regenerate_provenance() # re-derive rules from source
validate_provenance(payload) Prevention
- Always generate provenance via the official producer rather than hand-writing the JSON.
- Assert a non-empty rules list in CI before running validate_bundle.
- Never strip the 'rules' key in downstream transforms of the payload.
When it happens
Trigger: Calling validate_provenance(payload) or validate_bundle() with payload = {} , payload = {'rules': None}, {'rules': 'x'} (non-list), or {'rules': []} (empty list).
Common situations: Hand-authoring a provenance.json by hand and forgetting the rules array; a pipeline stage that failed to derive any rules still emitting a provenance file; serializing an empty rules list after a filter step removed all rules; renaming the 'rules' key in a producer without updating the consumer.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Invalid provenance metadata
- lacks probe evidence
- -32602
- a generated candidate cannot claim original-source identity
- anchor evidence source duration is not bound to its receipt…
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/34443e1f1692e2d0.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:262
anchor.get("evidence_time"), anchor.get("source_duration"),
label="anchor evidence time",
)
if reference_durations is not None:
digest = anchor.get("source_ref_sha256")
expected_duration = reference_durations.get(digest) if isinstance(digest, str) else None
if expected_duration is None or anchor.get("source_duration") != expected_duration:
raise ContractError("anchor evidence source duration is not bound to its receipt reference")
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"
)
View on GitHub (pinned to 8321021c54)