affaan-m/ECC · error · ValueError

Missing artifact binding

Error message

Missing artifact binding

What it means

Every provider_provenance must include an artifact binding: a dict with a 'path' plus expected fingerprint fields (bytes, etc.). The library throws this in _verify_binding when the binding value is missing or not a dict, because there is nothing to fingerprint-verify against.

Solutions

  1. Add an 'evidence' dict under provider_provenance containing 'path' and the recorded fingerprint fields (bytes, hash values)
  2. Regenerate the binding by re-fingerprinting the artifact with the library's fingerprint helper instead of hand-writing it
  3. If evidence cannot be supplied, mark the provenance verification as supplied_local_evidence_only by using evidence_path instead of verify mode

Example fix

// before
"provider_provenance": {"provider": "acme", "request_id": "r1"}
// after
"provider_provenance": {"provider": "acme", "request_id": "r1",
  "evidence": {"path": "artifacts/a1.png", "bytes": 1024, "sha256": "..."}}
Defensive patterns

Strategy: validation

Validate before calling

for a in assets:
    p = a.get("provider_provenance") or {}
    ev = p.get("evidence")
    if a.get("origin") == "external_result" and not (isinstance(ev, dict) and ev.get("path")):
        raise ValueError(f"asset {a['id']}: missing artifact binding (evidence dict with path)")

Type guard

def has_binding(p: dict | None) -> bool:
    return isinstance(p, dict) and isinstance(p.get("evidence"), dict) and bool(p["evidence"].get("path"))

Try / catch

try:
    ingest_assets(assets)
except ValueError as e:
    if str(e) == "Missing artifact binding":
        regenerate_bindings(assets); retry()
    else:
        raise

Prevention

When it happens

Trigger: provider_provenance lacking the 'evidence' key while verify=true; evidence set to null/string; validate_assets checking an asset whose binding was dropped during manifest editing.

Common situations: Hand-written manifests forgetting the evidence block; tooling serializing evidence as a stringified path; deleting evidence entries to 'clean up' manifests.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/9d09e1002beea58b. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/assets.py:129

        return None
    if not isinstance(source, dict):
        raise ValueError('external_result requires provider provenance and local evidence')
    provider = _text(source.get('provider'), 'provider')
    identifiers = {key: _text(source[key], key) for key in ('request_id', 'workflow_id')
                   if key in source}
    if not identifiers:
        raise ValueError('Provider provenance requires request_id or workflow_id')
    if verify:
        evidence = _verify_binding(source.get('evidence'), base)
    else:
        evidence = _fingerprint(_path(source.get('evidence_path'), base))
    return dict(provider=provider, **identifiers, evidence=evidence,
                verification='supplied_local_evidence_only')


def _verify_binding(value: Any, base: Path, modality: str | None = None) -> dict[str, Any]:
    if not isinstance(value, dict):
        raise ValueError('Missing artifact binding')
    actual = _fingerprint(_path(value.get('path'), base), modality)
    if type(value.get('bytes')) is not int or any(value.get(k) != v for k, v in actual.items()):
        raise ValueError(f'Artifact changed or binding invalid: {actual["path"]}')
    return actual


_TASTE_FIELDS = ('genre_number', 'genre_slug', 'style_fingerprint', 'reference_sha256')


def _bundle(value: Any, base: Path, verify: bool) -> tuple[dict[str, Any], dict[tuple[str, str], Any]]:
    from .contract import validate_bundle

    if verify:
        binding = _verify_binding(value, base)
        root = Path(binding['path']).parent
    else:
        root = _path(value, base)
        binding = _fingerprint(root / 'receipt.json')

View on GitHub (pinned to 8321021c54)