affaan-m/ECC · error · ContractError
receipt evidence_artifacts must be a list
Error message
receipt evidence_artifacts must be a list
What it means
validate_artifact_receipt checks that a receipt's 'evidence_artifacts' key is a list before binding each emitted artifact to its provenance. A missing, None, or non-list value raises this ContractError, since artifact binding cannot proceed.
Solutions
- Ensure 'evidence_artifacts' is a JSON array of artifact entry objects in the receipt.
- Fix the receipt producer so it always emits the list, even when empty (use [] rather than omitting the key).
- Check receipt file version/schema matches what this validator expects; regenerate the receipt with the current tool version.
- Confirm the file loaded as 'receipt' is actually a Tasteforge receipt document.
Example fix
// before
receipt = {"evidence_artifacts": {"out.png": {"sha256": "..."}}}
// after
receipt = {"evidence_artifacts": [{"path": "out.png", "sha256": "..."}]} Defensive patterns
Strategy: type-guard
Validate before calling
def receipt_loadable(data):
return isinstance(data, dict) and isinstance(data.get("evidence_artifacts"), list) Type guard
def is_receipt(data: object) -> bool:
return isinstance(data, dict) and isinstance(data.get("evidence_artifacts"), list) Try / catch
try:
validate_artifact_receipt(out_dir, receipt)
except ContractError as e:
if "evidence_artifacts must be a list" in str(e):
receipt = rebuild_receipt(out_dir) # regenerate from emitted artifacts
validate_artifact_receipt(out_dir, receipt)
else:
raise Prevention
- Always emit evidence_artifacts as a list, using [] when there are no artifacts — never omit the key.
- Validate the receipt against its schema right after writing it.
- Version the receipt schema and check the loaded file matches the validator's expected version.
When it happens
Trigger: Passing receipt = {} (no key), {'evidence_artifacts': None}, {'evidence_artifacts': {...}} (dict instead of list), or a string value to validate_artifact_receipt via validate_bundle.
Common situations: A receipt writer crashing before populating evidence_artifacts; hand-rolled receipt serialization emitting a dict keyed by artifact path instead of a list; schema drift between receipt producer and validator versions; loading the wrong JSON (a summary file instead of a receipt).
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- receipt evidence_artifacts entries must be objects
- artifact path must be a non-empty relative path
- artifact byte size does not match receipt
- artifact has invalid modality binding
- artifact has invalid reference path
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/46cfbbf612804ee8.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:312
or payload.get("provider_calls") != 0
or payload.get("provider_execution") is not False):
raise ContractError(f"{modality} manifest crosses the dry-run boundary")
for request in payload["requests"]:
if (request.get("dry_run") is not True
or request.get("submit") is not False
or type(request.get("provider_calls")) is not int
or request.get("provider_calls") != 0
or request.get("provider_execution") is not False
or request.get("provider_call_mode") != "disabled"):
raise ContractError(f"{modality} request crosses the dry-run boundary")
def validate_artifact_receipt(out_dir: str | Path, receipt: dict[str, Any]) -> None:
"""Verify that the receipt binds every emitted artifact and its provenance."""
out_dir = Path(out_dir).resolve()
entries = receipt.get("evidence_artifacts")
if not isinstance(entries, list):
raise ContractError("receipt evidence_artifacts must be a list")
if not all(isinstance(entry, dict) for entry in entries):
raise ContractError("receipt evidence_artifacts entries must be objects")
known_sources: set[tuple[str, str]] = set()
source_durations: dict[tuple[str, str], float] = {}
for key in ("references", "evidence_files"):
sources = receipt.get(key, [])
if not isinstance(sources, list):
raise ContractError(f"receipt {key} must be a list")
for source in sources:
if not isinstance(source, dict):
raise ContractError(f"receipt {key} contains an invalid source")
source_path = source.get("path")
expected_digest = source.get("sha256")
if (not isinstance(source_path, str) or not source_path
or not isinstance(expected_digest, str)
or not re.fullmatch(r"[0-9a-f]{64}", expected_digest)):
raise ContractError("receipt has an invalid source identity")
known_sources.add((source_path, expected_digest))View on GitHub (pinned to 8321021c54)