{"record":{"id":"85c10b9d829f05da","repo":"unslothai/unsloth","slug":"linked-folders-support-only-knowledge-base-and-pro","errorCode":null,"errorMessage":"Linked folders support only knowledge-base and project scopes","messagePattern":"Linked folders support only knowledge-base and project scopes","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"studio/backend/core/rag/folder_sync.py","lineNumber":225,"sourceCode":"                for encoded, decoded in escaped.items():\n                    path = path.replace(encoded, decoded)\n                points.add(_path_key(os.path.realpath(path)))\n    except OSError:\n        return frozenset()\n    return frozenset(points)\n\n\ndef create_folder(\n    *,\n    scope_type: str,\n    scope_id: str,\n    path: str,\n    expected_identity: tuple[int, int] | None = None,\n    name: str | None = None,\n    auto_sync: bool = True,\n) -> dict:\n    if scope_type not in {\"knowledge_base\", \"project\"}:\n        raise ValueError(\"Linked folders support only knowledge-base and project scopes\")\n    normalized = validate_folder_path(path)\n    try:\n        root_device, root_inode = _root_identity(normalized)\n    except RuntimeError as exc:\n        raise ValueError(str(exc)) from exc\n    if expected_identity is not None and (root_device, root_inode) != expected_identity:\n        raise ValueError(\"Linked folder changed after it was selected\")\n    scope = (\n        store.kb_scope(scope_id)\n        if scope_type == \"knowledge_base\"\n        else store.project_scope(scope_id)\n    )\n    folder_id = str(uuid.uuid4())\n    now = _now()\n    with _scope_lock(scope):\n        conn = rag_db.get_connection()\n        try:\n            conn.execute(\"BEGIN IMMEDIATE\")","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/rag/folder_sync.py#L207-L243","documentation":"create_folder() only accepts scope_type in {\"knowledge_base\", \"project\"}; any other scope string raises immediately. Linked folders are partitioned per KB or per project because those are the only scopes with rows in the linked_folders table (scope column) and retirement tracking (linked_folder_retired_scopes).","triggerScenarios":"Calling create_folder(scope_type=\"user\"|\"organization\"|\"chat\"|None, ...) with any value outside the two allowed strings; also duplicated by create_folder_with_sync (line 341) with the same guard.","commonSituations":"Frontend sends a new scope kind after an API change; a caller passes the raw scope string from a URL parameter without whitelisting; copy-pasted code from another feature that supports more scopes.","solutions":["Pass scope_type exactly 'knowledge_base' or 'project' and use store.kb_scope(scope_id)/store.project_scope(scope_id) accordingly.","Whitelist the scope in the API layer before calling create_folder so invalid values never reach the sync module.","If a new scope type is genuinely needed, extend the set in both create_folder and create_folder_with_sync plus scope resolution and retirement handling."],"exampleFix":"# before\ncreate_folder(scope_type=scope, scope_id=sid, path=p)  # scope may be 'user'\n\n# after\nif scope not in {\"knowledge_base\", \"project\"}:\n    raise HTTPException(400, \"unsupported scope\")\ncreate_folder(scope_type=scope, scope_id=sid, path=p)","handlingStrategy":"validation","validationCode":"_LINKABLE_SCOPES = {\"knowledge_base\", \"project\"}\n\ndef is_linkable_scope(scope_type: str) -> bool:\n    return scope_type in _LINKABLE_SCOPES","typeGuard":"def is_linkable_scope(scope_type: str | None) -> TypeGuard[Literal[\"knowledge_base\", \"project\"]]:\n    return scope_type in (\"knowledge_base\", \"project\")","tryCatchPattern":"try:\n    create_folder(scope_type=scope, ...)\nexcept ValueError as e:\n    if \"only knowledge-base and project\" in str(e):\n        return HTTP_422(...)  # client bug, do not retry\n    raise","preventionTips":["Whitelist scope_type in the API/CLI layer before it reaches folder_sync.","Type the parameter as Literal['knowledge_base', 'project'] so mypy catches bad callers.","Keep the whitelist in create_folder and create_folder_with_sync in sync when adding scopes."],"tags":["validation","api-contract","rag","folder-sync"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}