affaan-m/ECC · error · ValueError

external_result requires provider provenance and local…

Error message

external_result requires provider provenance and local evidence

What it means

An asset with origin 'external_result' MUST carry a dict-shaped 'provider_provenance'. The library throws this when provider_provenance is missing or not a dict, because external-origin assets are untrusted without documented provider evidence.

Solutions

  1. Add a provider_provenance dict with at least a 'provider' string plus request_id or workflow_id
  2. If the value is a JSON string, parse it into an object before ingestion
  3. Change origin to an internal value if the asset was not actually produced externally (then remove the provenance requirement path)

Example fix

// before
{"id": "a1", "origin": "external_result", "modality": "image"}
// after
{"id": "a1", "origin": "external_result", "modality": "image",
 "provider_provenance": {"provider": "acme", "request_id": "req-123", "evidence_path": "artifacts/a1.png"}}
Defensive patterns

Strategy: validation

Validate before calling

for a in assets:
    if a.get("origin") == "external_result" and not isinstance(a.get("provider_provenance"), dict):
        raise ValueError(f"asset {a['id']}: external_result needs a provider_provenance object")

Type guard

def has_provenance_dict(a: dict) -> bool:
    p = a.get("provider_provenance")
    return a.get("origin") != "external_result" or isinstance(p, dict)

Try / catch

try:
    ingest_assets(assets)
except ValueError as e:
    if "external_result requires provider provenance" in str(e):
        raise ManifestError("fill provider_provenance for all external_result assets") from e
    raise

Prevention

When it happens

Trigger: An asset with origin set to 'external_result' but no 'provider_provenance' key, or provider_provenance set to a string/list/None instead of a dict, passed to ingest_assets or validate_assets.

Common situations: Manually adding external assets without filling in provenance; a tool emitting provenance as a JSON string rather than an object; truncating provenance during serialization.

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/a665271f1bcec4b3. Report an issue: GitHub.

Appendix: source

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

        asset_id = _text(asset.get('id'), 'asset id')
        if asset_id in ids:
            raise ValueError(f'Duplicate asset id: {asset_id}')
        ids.add(asset_id)
        if asset.get('modality') not in tuple(MODALITIES):
            raise ValueError('modality must be image, video or 3d_asset')
        if asset.get('origin') not in tuple(ORIGINS):
            raise ValueError('Invalid asset origin')
    return value


def _provenance(asset: dict[str, Any], base: Path, verify: bool) -> dict[str, Any] | None:
    source = asset.get('provider_provenance')
    if asset['origin'] != 'external_result':
        if source is not None:
            raise ValueError('Provider provenance requires external_result origin')
        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()):

View on GitHub (pinned to 8321021c54)