{"record":{"id":"2627f499e879153d","repo":"HKUDS/DeepTutor","slug":"folder-does-not-exist-folder-2627f4","errorCode":null,"errorMessage":"Folder does not exist: {folder}","messagePattern":"Folder does not exist: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/rag/linked_kb.py","lineNumber":102,"sourceCode":"            continue\n        try:\n            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:","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/rag/linked_kb.py#L84-L120","documentation":"Raised by assert_path_allowed when the path given to link a folder (e.g. an Obsidian vault) into a knowledge base does not exist on disk after expanduser(). It is the first guard in the linked-folder validation chain (exists → is directory → within allowlist).","triggerScenarios":"Calling connect_obsidian_vault, probe_linked_folder_route, connect_linked_folder_route, or create_connection with a typo'd or nonexistent folder path; passing a relative path that doesn't resolve from the process CWD; the folder living on an unmounted drive.","commonSituations":"Typos in vault paths, ~ not expanded correctly, moving/renaming a vault after saving its path, running the server in a container where the host path isn't mounted.","solutions":["Verify the path exists and is spelled correctly (ls the exact expanded path).","Use an absolute path including ~ expansion, e.g. /home/user/Vault rather than ~/Vault if the shell isn't expanding it.","If running in Docker/WSL, confirm the folder is mounted into the container.","Reconnect via the linked-folder UI so the picker supplies a validated absolute path."],"exampleFix":"# before\nawait connect_obsidian_vault(session, kb_id, \"~/Notes/vualt\")  # typo\n# after\nawait connect_obsidian_vault(session, kb_id, str(Path(\"~/Notes/vault\").expanduser()))","handlingStrategy":"validation","validationCode":"from pathlib import Path\np = Path(folder_path).expanduser()\nif not p.exists():\n    raise InputError(f\"folder missing: {p}\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always build folder paths via Path(...).expanduser() before passing to linked-folder APIs.","Show a file picker that only allows directory selection in the UI."],"tags":["filesystem","path-validation","linked-folders","rag"],"backgroundTag":"path-not-found","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}