{"record":{"id":"dc6a1a2ac16f3134","repo":"HumanSignal/label-studio","slug":"absolute-local-path-self-path-must-be-a-subdir","errorCode":null,"errorMessage":"Absolute local path \"{self.path}\" must be a subdirectory of LOCAL_FILES_DOCUMENT_ROOT=\"{settings.LOCAL_FILES_DOCUMENT_ROOT}\" by security reasons. For example: \"{example_path}\".","messagePattern":"Absolute local path \"(.+?)\" must be a subdirectory of LOCAL_FILES_DOCUMENT_ROOT=\"(.+?)\" by security reasons\\. For example: \"(.+?)\"\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"label_studio/io_storages/localfiles/models.py","lineNumber":94,"sourceCode":"        return ''\n\n    def validate_connection(self):\n        normalized_path = self._get_storage_path_or_raise(ValidationError)\n        self.path = normalized_path\n        path = Path(normalized_path)\n        document_root = Path(settings.LOCAL_FILES_DOCUMENT_ROOT)\n        example_path = Path(settings.LOCAL_FILES_DOCUMENT_ROOT) / 'dataset1'\n\n        if not path.exists():\n            raise ValidationError(f'Absolute local path \"{self.path}\" does not exist')\n        if document_root == path:\n            raise ValidationError(\n                f'Absolute local path \"{self.path}\" cannot be the same as '\n                f'LOCAL_FILES_DOCUMENT_ROOT=\"{settings.LOCAL_FILES_DOCUMENT_ROOT}\" by security reasons. Please add a subdirectory. '\n                f'For example: \"{example_path}\".'\n            )\n        if document_root not in path.parents:\n            raise ValidationError(\n                f'Absolute local path \"{self.path}\" must be a subdirectory of '\n                f'LOCAL_FILES_DOCUMENT_ROOT=\"{settings.LOCAL_FILES_DOCUMENT_ROOT}\" by security reasons. '\n                f'For example: \"{example_path}\".'\n            )\n        if settings.LOCAL_FILES_SERVING_ENABLED is False:\n            raise ValidationError(\n                'Serving local files from the host filesystem can be a security risk, so '\n                'LOCAL_FILES_SERVING_ENABLED is disabled by default. '\n                'To enable Local Files storage, set the LOCAL_FILES_SERVING_ENABLED environment '\n                'variable to \"true\" and restart Label Studio. See '\n                'https://labelstud.io/guide/storage.html#Local-storage for details.'\n                '\\n\\n'\n                f'{self.community_auto_hint()}'\n            )\n\n\nclass LocalFilesImportStorageBase(LocalFilesMixin, ImportStorage):\n    url_scheme = 'https'","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/io_storages/localfiles/models.py#L76-L112","documentation":"Local Files storage paths must live inside LOCAL_FILES_DOCUMENT_ROOT; validate_connection checks document_root not in path.parents and raises this Django ValidationError otherwise. This confinement prevents path-traversal-style access to arbitrary host filesystem locations. It fires when the given absolute path is outside, above, or unrelated to the configured document root.","triggerScenarios":"Creating/updating a Local Files storage with a path like /home/user/data or /tmp/data while LOCAL_FILES_DOCUMENT_ROOT is /label-studio/data; using '..' or symlink tricks that resolve outside the root; changing LOCAL_FILES_DOCUMENT_ROOT after the storage was created without updating the path.","commonSituations":"Docker containers where the data volume was mounted at a different location than the path entered; local dev on macOS/Windows paths that don't match the server's document root; users unaware that LOCAL_FILES_DOCUMENT_ROOT must be set to cover their data directory.","solutions":["Either move/symlink the data under LOCAL_FILES_DOCUMENT_ROOT and use that path, or set LOCAL_FILES_DOCUMENT_ROOT to a common ancestor of the storage path and restart Label Studio","For Docker, mount the data at a path inside the document root (-v /your/data:/label-studio/data/local-files/<name>)","Avoid relative or ..-containing paths; always pass a normalized absolute subdirectory of the document root"],"exampleFix":"// before\nLOCAL_FILES_DOCUMENT_ROOT=/label-studio/data\n{ \"path\": \"/home/user/datasets/tweets\" }   // outside document root\n// after\nLOCAL_FILES_DOCUMENT_ROOT=/label-studio/data\n# mount: -v /home/user/datasets:/label-studio/data/local-files/datasets\n{ \"path\": \"/label-studio/data/local-files/datasets/tweets\" }","handlingStrategy":"validation","validationCode":"from pathlib import Path\np, root = Path(path), Path('/label-studio/data')\nif not (p.is_absolute() and root in p.parents):\n    raise SystemExit(f'Path must be a subdirectory of {root}')","typeGuard":null,"tryCatchPattern":"try:\n    storage.validate_connection()\nexcept ValidationError as e:\n    if 'must be a subdirectory of' in ';'.join(e.messages):\n        logger.error('Move data under %s or adjust LOCAL_FILES_DOCUMENT_ROOT', e)\n    else:\n        raise","preventionTips":["Mount Docker volumes inside the document root path","Re-check storage paths after changing LOCAL_FILES_DOCUMENT_ROOT","Resolve symlinks (Path.resolve()) to confirm the real location stays under the root","Avoid '..' or relative path components"],"tags":["security","local-storage","validation","configuration"],"backgroundTag":"path-outside-document-root","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}