affaan-m/ECC · error · ContractError

receipt must declare an explicit source availability policy

Error message

receipt must declare an explicit source availability policy

What it means

When a receipt declares any provenance sources (references or evidence_files are non-empty), it must also set "source_availability_policy" to exactly "allow_unavailable" or "require_available". If sources exist and the policy is missing or any other value, ContractError('receipt must declare an explicit source availability policy') is raised, forcing an explicit decision about how missing sources are handled.

Solutions

  1. Add "source_availability_policy": "require_available" (fail when sources are missing) or "allow_unavailable" (skip missing sources) to the receipt
  2. Correct the spelling/casing to one of the two exact allowed strings
  3. Regenerate the receipt with current tasteforge tooling so the field is emitted

Example fix

// before
{"references": [...]}  // no policy
// after
{"references": [...], "source_availability_policy": "require_available"}
Defensive patterns

Strategy: validation

Validate before calling

POLICIES = {'allow_unavailable', 'require_available'}

def policy_ok(receipt):
    has_sources = bool(receipt.get('references')) or bool(receipt.get('evidence_files'))
    return not has_sources or receipt.get('source_availability_policy') in POLICIES

Type guard

def declares_policy(receipt) -> bool:
    return receipt.get('source_availability_policy') in {'allow_unavailable', 'require_available'}

Try / catch

try:
    validate_artifact_receipt(receipt, out_dir)
except ContractError as e:
    if 'source availability policy' in str(e):
        receipt['source_availability_policy'] = 'require_available'
        validate_artifact_receipt(receipt, out_dir)
    raise

Prevention

When it happens

Trigger: A receipt listing references/evidence_files but omitting "source_availability_policy"; the key set to null, "", or a typo like "allow-unavailable"/"RequireAvailable"; sources added to an older receipt that never carried the policy field.

Common situations: Upgrading receipts from an older tasteforge format without the policy field; typos or casing mistakes in the policy string; template-generated receipts that only set the policy conditionally.

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

Appendix: source

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

            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))
            if key == "references":
                source_duration = source.get("source_duration")
                if not _is_finite_real(source_duration):
                    raise ContractError("receipt reference has an invalid finite source duration")
                source_duration = cast(float, source_duration)
                if float(source_duration) <= 0:
                    raise ContractError("receipt reference has an invalid finite source duration")
                source_durations[(source_path, expected_digest)] = float(source_duration)
                _validate_probe_evidence(
                    source.get("probe"), float(source_duration), label="receipt reference"
                )
    source_policy = receipt.get("source_availability_policy")
    if known_sources and source_policy not in {"allow_unavailable", "require_available"}:
        raise ContractError("receipt must declare an explicit source availability policy")
    for source_path, expected_digest in sorted(known_sources):
        path = Path(source_path)
        try:
            metadata = path.lstat()
        except FileNotFoundError:
            if source_policy == "require_available":
                raise ContractError(f"receipt source is unavailable: {source_path}") from None
            continue
        if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode):
            raise ContractError(f"receipt source is not a safe regular file: {source_path}")
        try:
            actual_digest = _sha256(path)
        except FileNotFoundError:
            if source_policy == "require_available":
                raise ContractError(f"receipt source is unavailable: {source_path}") from None
            continue
        except OSError:
            raise ContractError(f"receipt source cannot be securely read: {source_path}") from None

View on GitHub (pinned to 8321021c54)