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
- Verify the path exists: ls <bundle_root>; fix typos or run from the correct directory
- Run the generation step first so the output bundle is actually produced
- Check whether an earlier pipeline stage deleted/cleaned the output dir before validation
- 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
- Always run generation before validation in pipelines
- Use absolute paths for bundle roots in CI
- Check the generation step's exit status before validating
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
- Nasiko cannot install directly into a filesystem root.
- output bundle root must be a directory
- output intermediate must be a directory
- output root must be a directory
- all overlays must be readable local files
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)