affaan-m/ECC · error · ContractError
output bundle contains a special file
Error message
output bundle contains a special file: {entry.path} What it means
During the bundle walk, any entry that is neither a symlink, directory, nor regular file is rejected as a 'special file' (FIFOs, sockets, device nodes). The contract requires the bundle to contain only plain directories and files so packaging/hashing is deterministic.
Solutions
- Delete the named special file at the reported path and rerun validation
- Find the process/tool that created the pipe/socket in the output dir and fix its cleanup
- Clean the output directory fully (rm -rf and regenerate) before validating
- Add a pre-validation sweep: find <bundle> ! -type f ! -type d -delete
Example fix
# before
mkfifo dist/bundle/pipe && validate_bundle(Path('dist/bundle'))
# after
rm -f dist/bundle/pipe && validate_bundle(Path('dist/bundle')) Defensive patterns
Strategy: validation
Validate before calling
import os
special = [os.path.join(d, n) for d, ds, fs in os.walk(bundle_root)
for n in ds + fs
if not os.path.isfile(os.path.join(d, n)) and not os.path.isdir(os.path.join(d, n)) and not os.path.islink(os.path.join(d, n))]
assert not special, f'special files in bundle: {special}' Try / catch
try:
validate_bundle(root)
except ContractError as e:
if 'special file' in str(e):
print(f'Remove the special file and fix its creator: {e}')
else:
raise Prevention
- Clean output dirs (rm -rf) before regeneration
- Ensure dev servers/tests don't leave sockets or FIFOs in dist/
- Sweep with: find <bundle> ! -type f ! -type d
When it happens
Trigger: A FIFO, Unix socket, or device node exists inside the output bundle when validate_bundle walks it — typically left behind by a build/test process that created a pipe or socket in the output dir and didn't clean up.
Common situations: Dev servers leaving .sock files in a dist directory, mkfifo used in test fixtures, or a builder accidentally writing through a device path.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- all overlays must be readable local files
- all takes must be readable local files
- artifact path must stay beneath output root
- base video must be a readable local file
- FileNotFoundError(source)
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/8c9d85f76315081a.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/contract.py:114
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:
"""Require complete, semantically distinct numbered genre specs."""
if not specs:
raise ContractError("at least one genre spec is required")
numbers = [spec.get("number") for spec in specs]
if len(numbers) != len(set(numbers)):
raise ContractError("genre numbers must be distinct")
fingerprints = [spec.get("style_fingerprint") for spec in specs]
signatures = [_semantic_signature(spec) for spec in specs]
if len(fingerprints) != len(set(fingerprints)) or len(signatures) != len(set(signatures)):
raise ContractError("genre references collapsed into a generic style; distinct specs required")
for spec in specs:
if spec.get("dry_run") is not True:
raise ContractError(f"genre {spec.get('number')} crosses the dry-run boundary")View on GitHub (pinned to 8321021c54)