affaan-m/ECC · error · ValueError
missing output directory
Error message
missing output directory: {part} What it means
While walking the output tree with create=False, _open_dir expects every intermediate directory to already exist. If os.stat (no-follow, dir_fd-relative) reports FileNotFoundError for a component, the walk converts it into this ValueError naming the missing component instead of silently creating directories the caller did not ask for. Use create=True (as prepare does) when the tree should be built.
Solutions
- Call safe.prepare(parts) (create=True) for the directory prefix before writing files into it
- Check the path spelling/case of the intermediate directory
- Re-run the earlier workflow stage that creates the directory
- If the dir should exist, use _open_dir/create mode via prepare rather than write_json directly
Example fix
# before
safe.write_json(('runs', 'run1', 'summary.json'), data) # runs/run1 missing
# after
safe.prepare(('runs', 'run1'))
safe.write_json(('runs', 'run1', 'summary.json'), data) Defensive patterns
Strategy: validation
Validate before calling
def ensure_dirs_prepared(safe, parts):
safe.prepare(tuple(parts[:-1])) # create intermediate dirs before writing Type guard
def exists_under_root(safe, parts) -> bool:
try:
safe._open_dir(tuple(parts), create=False)
return True
except ValueError:
return False Try / catch
try:
safe.write_json(parts, data)
except ValueError as e:
if e.args[0].startswith('missing output directory'):
safe.prepare(tuple(parts[:-1]))
safe.write_json(parts, data)
else:
raise Prevention
- Always call prepare() for a subdirectory tree before writing into it
- Do not delete output directories between workflow stages
- Watch for case-sensitivity mismatches on Linux (Styles vs styles)
- Keep a single owner of the output-tree layout to avoid ad-hoc paths
When it happens
Trigger: Calling write_json or artifact_metadata with a subdirectory path whose intermediate directory was never created via prepare; referencing a component that was deleted after preparation; a typo in the directory component.
Common situations: Writing into outputs/<run>/styles before calling prepare for that subdirectory; cleaning the output tree between steps; case-sensitivity mismatch (Styles vs styles) on Linux.
Related errors
- Failed to create directory
- output parent directory must already exist
- Agents directory not found
- all overlays must be readable local files
- all takes must be readable local files
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/7b5c28572fbf0177.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/workflow.py:207
def close(self) -> None:
if self._root_fd >= 0:
os.close(self._root_fd)
self._root_fd = -1
def __del__(self) -> None:
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
View on GitHub (pinned to 8321021c54)