affaan-m/ECC · error · ContractError

artifact has invalid modality binding

Error message

artifact {relative} has invalid modality binding

What it means

Each artifact's 'modalities' field must be a list whose members are all drawn from the library's required modality set (_REQUIRED_MODALITIES). This ContractError is raised when modalities is missing, not a list, or contains any unknown/unsupported modality name.

Solutions

  1. Correct each artifact's modalities list to use only the exact enum values defined by the library.
  2. Regenerate the receipt with tasteforge so modalities are written from the known set.
  3. Inspect the allowed values (e.g. print _REQUIRED_MODALITIES in contract.py) and diff against your receipt.

Example fix

// before
{"modalities": ["Image", "text "]}
// after
{"modalities": ["image", "text"]}
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = {'image', 'text', 'audio', 'video'}  # mirror _REQUIRED_MODALITIES
for i, e in enumerate(receipt['artifacts']):
    m = e.get('modalities')
    if not isinstance(m, list) or any(x not in ALLOWED for x in m):
        raise SystemExit(f'artifact[{i}] has invalid modalities: {m}')

Type guard

def has_valid_modalities(entry: dict, allowed: set[str]) -> bool:
    m = entry.get('modalities')
    return isinstance(m, list) and all(isinstance(x, str) and x in allowed for x in m)

Try / catch

try:
    validate_artifact_receipt(out_dir, entries)
except ContractError as e:
    if 'invalid modality binding' in str(e):
        regenerate_receipt(receipt_path)  # enum names come from the generator
    else:
        raise

Prevention

When it happens

Trigger: Calling validate_artifact_receipt()/validate_bundle() with receipt entries where 'modalities' is absent, a non-list, or contains a modality string not in the allowed enum (typo, renamed modality, custom value).

Common situations: Typos like "image " or "Image" vs the canonical lowercase enum; a tool version that renamed modalities; hand-authored receipt entries guessing at modality names.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/contract.py:401

    if extra:
        raise ContractError(f"receipt binds missing artifact: {sorted(extra)}")

    for entry in entries:
        relative = entry.get("path")
        assert isinstance(relative, str)
        path = (out_dir / relative).resolve()
        try:
            path.relative_to(out_dir)
        except ValueError as error:
            raise ContractError(f"artifact path escapes output directory: {relative}") from error
        if entry.get("provider_execution") is not False:
            raise ContractError(f"artifact {relative} permits provider execution")
        if not isinstance(entry.get("genre_numbers"), list):
            raise ContractError(f"artifact {relative} lacks genre binding")
        modalities = entry.get("modalities")
        if (not isinstance(modalities, list)
                or any(modality not in _REQUIRED_MODALITIES for modality in modalities)):
            raise ContractError(f"artifact {relative} has invalid modality binding")
        if entry.get("bytes") != path.stat().st_size:
            raise ContractError(f"artifact {relative} byte size does not match receipt")
        if entry.get("sha256") != _sha256(path):
            raise ContractError(f"artifact {relative} SHA-256 does not match receipt")
        provenance = entry.get("provenance")
        if not isinstance(provenance, list) or not provenance:
            raise ContractError(f"artifact {relative} lacks exact reference/time provenance")
        for source in provenance:
            if not isinstance(source.get("reference_path"), str) or not source["reference_path"]:
                raise ContractError(f"artifact {relative} has invalid reference path")
            digest = source.get("reference_sha256")
            if not isinstance(digest, str) or len(digest) != 64:
                raise ContractError(f"artifact {relative} has invalid reference SHA-256")
            if (source["reference_path"], digest) not in known_sources:
                raise ContractError(f"artifact {relative} cites an unknown provenance source")
            times = source.get("reference_times")
            basis = source.get("time_basis")
            if not isinstance(times, list) or basis not in {"media_seconds", "whole_file"}:

View on GitHub (pinned to 8321021c54)