affaan-m/ECC · error · ContractError

receipt contains duplicate artifact paths

Error message

receipt contains duplicate artifact paths

What it means

The receipt must bind each emitted artifact exactly once. This ContractError is raised when two or more entries in the artifacts array share the same 'path' string, detected by comparing the list length against the set of unique paths. Duplicates indicate a corrupted or hand-crafted receipt.

Solutions

  1. Remove the duplicate artifact entries so each path appears once.
  2. Regenerate the receipt with tasteforge to produce a canonical artifacts list.
  3. Deduplicate programmatically before validation, e.g. jq sorting artifacts by .path and dropping repeats.

Example fix

// before
{"artifacts": [{"path": "a.png", ...}, {"path": "a.png", ...}]}
// after
{"artifacts": [{"path": "a.png", ...}]}
Defensive patterns

Strategy: validation

Validate before calling

paths = [e.get('path') for e in receipt['artifacts']]
if len(paths) != len(set(paths)):
    dupes = sorted({p for p in paths if paths.count(p) > 1})
    raise SystemExit(f'duplicate receipt paths: {dupes}')

Type guard

def paths_are_unique(entries: list[dict]) -> bool:
    paths = [e.get('path') for e in entries if isinstance(e.get('path'), str)]
    return len(paths) == len(set(paths))

Try / catch

try:
    validate_artifact_receipt(out_dir, entries)
except ContractError as e:
    if 'duplicate artifact paths' in str(e):
        dedupe_and_rewrite_receipt(receipt_path)
    else:
        raise

Prevention

When it happens

Trigger: Calling validate_artifact_receipt()/validate_bundle() on a receipt.json where artifacts[] contains the same path value more than once (e.g. duplicated entries from a merge or scripted edit).

Common situations: Merging two receipts or re-running a generation script that appends instead of replaces; manual copy-paste of an entry; a buggy custom post-processor that duplicates artifacts.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

            continue
        except OSError:
            raise ContractError(f"receipt source cannot be securely read: {source_path}") from None
        if actual_digest != expected_digest:
            raise ContractError(f"receipt source SHA-256 changed after generation: {source_path}")

    emitted = {
        path.relative_to(out_dir).as_posix()
        for path in out_dir.rglob("*")
        if path.is_file() and path.name != "receipt.json"
    }
    bound_paths: list[str] = []
    for entry in entries:
        relative = entry.get("path")
        if not isinstance(relative, str) or not relative:
            raise ContractError("artifact path must be a non-empty relative path")
        bound_paths.append(relative)
    if len(bound_paths) != len(set(bound_paths)):
        raise ContractError("receipt contains duplicate artifact paths")
    missing = emitted - set(bound_paths)
    extra = set(bound_paths) - emitted
    if missing:
        raise ContractError(f"unbound emitted artifact: {sorted(missing)}")
    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):

View on GitHub (pinned to 8321021c54)