affaan-m/ECC · error · ContractError

output bundle is missing

Error message

output bundle is missing

What it means

_validate_output_tree checks the output bundle root exists via lstat before parsing content. If the root path does not exist at all, FileNotFoundError is caught and re-raised as this ContractError. It exists so bundle validation fails with a clear, library-specific message instead of a raw filesystem traceback.

Solutions

  1. Verify the path exists: ls <bundle_root>; fix typos or run from the correct directory
  2. Run the generation step first so the output bundle is actually produced
  3. Check whether an earlier pipeline stage deleted/cleaned the output dir before validation
  4. Make the bundle path configurable/absolute in CI to avoid relative-path mismatches

Example fix

# before
validate_bundle(Path('dist/bundle'))  # never generated
# after
from pathlib import Path
bundle = Path('dist/bundle')
if not bundle.exists():
    raise SystemExit(f'bundle not generated: run generate first ({bundle})')
validate_bundle(bundle)
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
bundle = Path(bundle_arg)
if not bundle.exists():
    raise SystemExit(f'output bundle missing: {bundle}')

Try / catch

try:
    validate_bundle(root)
except ContractError as e:
    if 'output bundle is missing' in str(e):
        print(f'Bundle not found at {root}; run the generation step first')
    else:
        raise

Prevention

When it happens

Trigger: Calling validate_bundle with a root path that was never created — e.g. the generation step failed or was skipped, or the path string has a typo.

Common situations: Deleting the output directory before validation in CI, running validation from a different working directory than the one used to generate the bundle, typos in CLI/config paths, or a failed generation run silently producing nothing.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

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

            if not chunk:
                break
            digest.update(chunk)
    finally:
        os.close(descriptor)
    return digest.hexdigest()


def _semantic_signature(spec: dict[str, Any]) -> str:
    signature = spec.get("signature", {})
    return json.dumps(signature, sort_keys=True, separators=(",", ":"))


def _validate_output_tree(root: Path) -> None:
    """Reject symlinks and special files before parsing bundle content."""
    try:
        metadata = root.lstat()
    except FileNotFoundError:
        raise ContractError("output bundle is missing") from None
    if stat.S_ISLNK(metadata.st_mode):
        raise ContractError("output bundle root must not be a symlink")
    if not stat.S_ISDIR(metadata.st_mode):
        raise ContractError("output bundle root must be a directory")
    pending = [root]
    while pending:
        directory = pending.pop()
        with os.scandir(directory) as entries:
            for entry in entries:
                if entry.is_symlink():
                    raise ContractError(f"output bundle contains a symlink: {entry.path}")
                if entry.is_dir(follow_symlinks=False):
                    pending.append(Path(entry.path))
                elif not entry.is_file(follow_symlinks=False):
                    raise ContractError(f"output bundle contains a special file: {entry.path}")


def validate_genre_specs(specs: list[dict[str, Any]]) -> None:

View on GitHub (pinned to 8321021c54)