{"record":{"id":"44ea936987e80c7f","repo":"HKUDS/DeepTutor","slug":"not-a-directory-folder","errorCode":null,"errorMessage":"Not a directory: {folder}","messagePattern":"Not a directory: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/rag/linked_kb.py","lineNumber":104,"sourceCode":"            roots.append(Path(chunk).expanduser().resolve())\n        except OSError:\n            continue\n    return roots\n\n\ndef assert_path_allowed(folder_path: str) -> Path:\n    \"\"\"Resolve ``folder_path`` and enforce the optional root allowlist.\n\n    Returns the resolved absolute path. Raises ``ValueError`` if the folder is\n    missing/not a directory, or escapes the configured allowlist (symlinks are\n    resolved first so they can't tunnel out). With no allowlist set, any\n    existing directory is permitted — the self-hosted default.\n    \"\"\"\n    folder = Path(folder_path).expanduser()\n    if not folder.exists():\n        raise ValueError(f\"Folder does not exist: {folder}\")\n    if not folder.is_dir():\n        raise ValueError(f\"Not a directory: {folder}\")\n    resolved = folder.resolve()\n\n    roots = allowed_link_roots()\n    if roots and not any(_is_within(resolved, root) for root in roots):\n        raise ValueError(\"This folder is outside the locations allowed for linking.\")\n    return resolved\n\n\ndef _is_within(path: Path, root: Path) -> bool:\n    try:\n        return path == root or root in path.parents\n    except OSError:\n        return False\n\n\ndef probe_linked_folder(folder_path: str, provider: str) -> ProbeResult:\n    \"\"\"Inspect ``folder_path`` for a ready ``provider`` index.\n","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/rag/linked_kb.py#L86-L122","documentation":"Raised by assert_path_allowed when the given folder exists but is not a directory (e.g. it is a regular file, socket, or broken special node). This is the second guard after existence, before symlink resolution and allowlist checks.","triggerScenarios":"Passing a file path (e.g. a .zip or .md file) where a folder is expected to connect_obsidian_vault / connect_linked_folder_route / create_connection; pointing at a FIFO or device node.","commonSituations":"Users pasting a file path from their file manager instead of its parent folder; automated scripts deriving the path from a file variable.","solutions":["Check with Path(p).is_dir() and pass the containing directory instead.","If linking a single file is intended, use the document upload API rather than linked folders.","Fix the source of the path in your script/UI so it selects directories only."],"exampleFix":"# before\nfolder = \"/home/user/notes/index.md\"\n# after\nfolder = \"/home/user/notes\"","handlingStrategy":"validation","validationCode":"if not Path(folder_path).expanduser().is_dir():\n    raise InputError(\"linked folder must be a directory\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate is_dir() client-side; reject file paths in the selection UI."],"tags":["filesystem","path-validation","linked-folders","rag"],"backgroundTag":"path-validation-failed","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}