{"record":{"id":"29f6e1d60e23c21e","repo":"invoke-ai/InvokeAI","slug":"empty-path-segments-not-allowed-in-subfolder-path-29f6e1","errorCode":null,"errorMessage":"Empty path segments not allowed in subfolder path","messagePattern":"Empty path segments not allowed in subfolder path","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"invokeai/app/services/video_files/video_files_disk.py","lineNumber":218,"sourceCode":"\n    def validate_path(self, path: Union[str, Path]) -> bool:\n        path = path if isinstance(path, Path) else Path(path)\n        return path.exists()\n\n    @staticmethod\n    def _validate_subfolder(subfolder: str) -> None:\n        \"\"\"Validates a subfolder path to prevent directory traversal.\"\"\"\n        if not subfolder:\n            return\n        if \"\\\\\" in subfolder:\n            raise ValueError(\"Backslashes not allowed in subfolder path\")\n        if subfolder.startswith(\"/\"):\n            raise ValueError(\"Absolute paths not allowed in subfolder path\")\n        for part in subfolder.split(\"/\"):\n            if part == \"..\":\n                raise ValueError(\"Parent directory references not allowed in subfolder path\")\n            if part == \"\":\n                raise ValueError(\"Empty path segments not allowed in subfolder path\")\n\n    def __get_sidecar_path(self, video_name: str, video_subfolder: str = \"\") -> Path:\n        sidecar_name = Path(video_name).stem + \".json\"\n        if video_subfolder:\n            self._validate_subfolder(video_subfolder)\n            sidecar_path = self.__sidecars_folder / video_subfolder / sidecar_name\n        else:\n            sidecar_path = self.__sidecars_folder / sidecar_name\n        resolved_base = self.__sidecars_folder.resolve()\n        resolved_sidecar_path = sidecar_path.resolve()\n        if not resolved_sidecar_path.is_relative_to(resolved_base):\n            raise ValueError(\"Sidecar path outside outputs folder, potential directory traversal detected\")\n        return resolved_sidecar_path\n\n    def __read_sidecar(self, video_name: str, video_subfolder: str = \"\") -> Optional[dict]:\n        path = self.__get_sidecar_path(video_name, video_subfolder=video_subfolder)\n        if not path.exists():\n            return None","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/services/video_files/video_files_disk.py#L200-L236","documentation":"_validate_subfolder rejects empty path segments: a subfolder like 'a//b' or a trailing slash 'a/' splits into '' parts, which is treated as malformed input rather than silently collapsed. The library requires a clean, canonical relative path.","triggerScenarios":"Passing subfolder='a//b', 'a/', '/a' (leading slash handled separately but trailing still fails), or '' inside get_path / __get_sidecar_path.","commonSituations":"String concatenation building subpaths ('base' + '/' + name where name already has a slash), URL path segments pasted into config, trailing-slash forms submitted from clients.","solutions":["Strip duplicate and trailing slashes: subfolder = '/'.join(p for p in raw.split('/') if p)","Normalize input with posixpath.normpath and re-check it is relative and non-empty","Fix the code that constructs the subfolder string so it never introduces empty segments"],"exampleFix":"// before\nsubfolder = f\"{base}/{name}/\"\n// after\nsubfolder = \"/\".join(p for p in f\"{base}/{name}\".split(\"/\") if p)","handlingStrategy":"validation","validationCode":"import posixpath\ndef normalize_subfolder(raw: str) -> str:\n    cleaned = \"/\".join(p for p in raw.split(\"/\") if p not in (\"\", \".\"))\n    assert \"..\" not in cleaned.split(\"/\"), \"traversal not allowed\"\n    return cleaned","typeGuard":"def is_clean_path(sub: str) -> bool:\n    return sub != \"\" and \"//\" not in sub and not sub.endswith(\"/\")","tryCatchPattern":"try:\n    path = service.get_path(video_name, subfolder=raw)\nexcept ValueError as e:\n    if \"Empty path segments\" in str(e):\n        path = service.get_path(video_name, subfolder=normalize_subfolder(raw))\n    else:\n        raise","preventionTips":["Join path components with a helper instead of string concatenation","Trim trailing slashes from client input before storing","Write unit tests for subfolder strings with double slashes and trailing slashes"],"tags":["filesystem","validation","path"],"backgroundTag":"invalid-path-segment","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}