affaan-m/ECC · error · ValueError

Provider provenance requires request_id or workflow_id

Error message

Provider provenance requires request_id or workflow_id

What it means

A provider_provenance block must contain at least one traceable identifier: request_id or workflow_id. The library throws this when neither key is present (or both fail _text validation), because provenance without an identifier cannot be correlated with the provider.

Solutions

  1. Add 'request_id' (or 'workflow_id') from the provider API response to the provider_provenance dict
  2. Ensure the identifier value is a non-empty string, not empty or whitespace
  3. Persist the provider response's ids at call time so they are available when building the manifest

Example fix

// before
"provider_provenance": {"provider": "acme"}
// after
"provider_provenance": {"provider": "acme", "request_id": "req-123"}
Defensive patterns

Strategy: validation

Validate before calling

for a in assets:
    p = a.get("provider_provenance") or {}
    if a.get("origin") == "external_result" and not (p.get("request_id") or p.get("workflow_id")):
        raise ValueError(f"asset {a['id']}: provenance needs request_id or workflow_id")

Type guard

def has_trace_id(p: dict | None) -> bool:
    return bool(p) and bool(p.get("request_id") or p.get("workflow_id"))

Try / catch

try:
    ingest_assets(assets)
except ValueError as e:
    if "requires request_id or workflow_id" in str(e):
        backfill_ids_from_provider_logs(assets); retry()
    else:
        raise

Prevention

When it happens

Trigger: provider_provenance dict supplied with only a 'provider' name and no request_id/workflow_id; keys present but with empty/whitespace values rejected by _text, leaving identifiers empty.

Common situations: Recording only the vendor name when logging external generations; losing the request id because the caller didn't persist the provider API response; empty-string ids from template placeholders.

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

Appendix: source

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

            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()):
        raise ValueError(f'Artifact changed or binding invalid: {actual["path"]}')
    return actual


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

View on GitHub (pinned to 8321021c54)