affaan-m/ECC · error · ValueError
output intermediate must be a directory
Error message
output intermediate must be a directory: {part} What it means
tasteforge's _open_dir walks each intermediate path component and creates directories safely with dir_fd and O_NOFOLLOW. If an intermediate component exists but is not a directory (e.g. a regular file or symlink-to-file), it refuses to proceed with this ValueError rather than overwriting or following it.
Solutions
- Inspect the output root and remove/rename the conflicting non-directory entry at the reported path segment
- Check the workflow config's output layout for a path whose parent name collides with an existing file
- Re-run the workflow with a clean output directory
Example fix
// before: output root contains a regular file named 'assets' // $ rm ./output/assets (or mv it elsewhere) mkdir -p output/assets // after: re-run the workflow
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
p = Path(output_root)
assert p.is_dir(), f"output root missing: {p}"
rel = Path(relative)
assert not rel.is_absolute()
for part in rel.parts[:-1]:
target = p / part
if target.exists():
assert target.is_dir() and not target.is_symlink(), f"{target} is not a directory" Type guard
def is_valid_artifact_dir(root: Path, rel: Path) -> bool:
if rel.is_absolute() or not rel.name:
return False
d = root / rel.parent
return d.exists() and d.is_dir() and not d.is_symlink() Prevention
- Keep artifact subdirectories dedicated to tasteforge output
- Never place plain files at names used as directory segments in artifact paths
- Clean the output root between runs
When it happens
Trigger: Calling write_json or artifact_metadata with a relative path whose parent segment collides with an existing non-directory entry in the output root, e.g. artifact path 'assets/file.json' where 'assets' is a regular file.
Common situations: A stale artifact or lock file occupies a name that config now treats as a directory; a previous run wrote a file where a subdirectory was later expected; path segments changed between workflow versions.
Related errors
- Nasiko cannot install directly into a filesystem root.
- output bundle is missing
- output bundle root 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/8181260f54de4c03.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/workflow.py:213
self.close()
def _open_dir(self, parts: tuple[str, ...], *, create: bool) -> int:
current = os.dup(self._root_fd)
try:
for part in parts:
if not part or part in {".", ".."} or "/" in part:
raise ValueError("output path contains an invalid component")
try:
metadata = os.stat(part, dir_fd=current, follow_symlinks=False)
except FileNotFoundError:
if not create:
raise ValueError(f"missing output directory: {part}") from None
os.mkdir(part, mode=0o700, dir_fd=current)
metadata = os.stat(part, dir_fd=current, follow_symlinks=False)
if stat.S_ISLNK(metadata.st_mode):
raise ValueError(f"output directory must not be a symlink: {part}")
if not stat.S_ISDIR(metadata.st_mode):
raise ValueError(f"output intermediate must be a directory: {part}")
child = os.open(
part,
os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW,
dir_fd=current,
)
os.close(current)
current = child
return current
except Exception:
os.close(current)
raise
def prepare(self, directories: tuple[str, ...]) -> None:
"""Validate every known intermediate before the first artifact write."""
opened: list[int] = []
try:
for directory in directories:
opened.append(self._open_dir((directory,), create=True))View on GitHub (pinned to 8321021c54)