{"record":{"id":"7b5c28572fbf0177","repo":"affaan-m/ECC","slug":"missing-output-directory-part","errorCode":null,"errorMessage":"missing output directory: {part}","messagePattern":"missing output directory: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/workflow.py","lineNumber":207,"sourceCode":"    def close(self) -> None:\n        if self._root_fd >= 0:\n            os.close(self._root_fd)\n            self._root_fd = -1\n\n    def __del__(self) -> None:\n        self.close()\n\n    def _open_dir(self, parts: tuple[str, ...], *, create: bool) -> int:\n        current = os.dup(self._root_fd)\n        try:\n            for part in parts:\n                if not part or part in {\".\", \"..\"} or \"/\" in part:\n                    raise ValueError(\"output path contains an invalid component\")\n                try:\n                    metadata = os.stat(part, dir_fd=current, follow_symlinks=False)\n                except FileNotFoundError:\n                    if not create:\n                        raise ValueError(f\"missing output directory: {part}\") from None\n                    os.mkdir(part, mode=0o700, dir_fd=current)\n                    metadata = os.stat(part, dir_fd=current, follow_symlinks=False)\n                if stat.S_ISLNK(metadata.st_mode):\n                    raise ValueError(f\"output directory must not be a symlink: {part}\")\n                if not stat.S_ISDIR(metadata.st_mode):\n                    raise ValueError(f\"output intermediate must be a directory: {part}\")\n                child = os.open(\n                    part,\n                    os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW,\n                    dir_fd=current,\n                )\n                os.close(current)\n                current = child\n            return current\n        except Exception:\n            os.close(current)\n            raise\n","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/workflow.py#L189-L225","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nsafe.write_json(('runs', 'run1', 'summary.json'), data)  # runs/run1 missing\n\n# after\nsafe.prepare(('runs', 'run1'))\nsafe.write_json(('runs', 'run1', 'summary.json'), data)","handlingStrategy":"validation","validationCode":"def ensure_dirs_prepared(safe, parts):\n    safe.prepare(tuple(parts[:-1]))  # create intermediate dirs before writing","typeGuard":"def exists_under_root(safe, parts) -> bool:\n    try:\n        safe._open_dir(tuple(parts), create=False)\n        return True\n    except ValueError:\n        return False","tryCatchPattern":"try:\n    safe.write_json(parts, data)\nexcept ValueError as e:\n    if e.args[0].startswith('missing output directory'):\n        safe.prepare(tuple(parts[:-1]))\n        safe.write_json(parts, data)\n    else:\n        raise","preventionTips":["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"],"tags":["filesystem","directory","not-found"],"backgroundTag":"directory-not-found","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}