affaan-m/ECC · error · ContractError

artifact path must be a non-empty relative path

Error message

artifact path must be a non-empty relative path

What it means

Each artifact entry in receipt.json must carry a 'path' that is a non-empty string. This ContractError is raised when an entry's path is missing, null, an empty string, or not a string at all. The library requires a well-formed relative path to bind the receipt entry to an emitted file under the output directory.

Solutions

  1. Fix the receipt entry so every artifact has a non-empty string 'path' relative to the output directory.
  2. Regenerate the receipt with tasteforge instead of editing it by hand.
  3. Validate receipt.json structure (e.g. jq '.artifacts[] | select(.path == null or .path == "")') before running the validator.

Example fix

// before
{"artifacts": [{"bytes": 120, "sha256": "abc..."}]}
// after
{"artifacts": [{"path": "images/hero.png", "bytes": 120, "sha256": "abc..."}]}
Defensive patterns

Strategy: validation

Validate before calling

for i, entry in enumerate(receipt.get('artifacts', [])):
    p = entry.get('path')
    if not isinstance(p, str) or not p:
        raise SystemExit(f'artifact[{i}].path must be a non-empty string')

Type guard

def has_valid_path(entry: dict) -> bool:
    return isinstance(entry.get('path'), str) and len(entry['path']) > 0

Try / catch

try:
    validate_artifact_receipt(out_dir, entries)
except ContractError as e:
    if 'non-empty relative path' in str(e):
        fix_receipt_paths(receipt_path)  # repair or regenerate
    else:
        raise

Prevention

When it happens

Trigger: Calling validate_artifact_receipt()/validate_bundle() with a hand-edited or programmatically generated receipt.json whose artifacts[] entry lacks 'path', has path: "", or path set to a non-string value (number, null, object).

Common situations: Manually pruning or editing receipt.json entries and accidentally deleting or emptying the path field; a custom generation script writing malformed entries; JSON schema drift between tool versions.

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

Appendix: source

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

        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
        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

View on GitHub (pinned to 8321021c54)