{"record":{"id":"da9c57cb6ae9fceb","repo":"unslothai/unsloth","slug":"knowledge-base-is-being-deleted","errorCode":null,"errorMessage":"Knowledge base is being deleted","messagePattern":"Knowledge base is being deleted","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"warning","filePath":"studio/backend/routes/rag.py","lineNumber":479,"sourceCode":"\n\n@router.delete(\"/knowledge-bases/{kb_id}\")\ndef delete_knowledge_base(kb_id: str, subject: str = Depends(get_current_subject)) -> dict:\n    _require_rag()\n    with _rag_unavailable_as_503():\n        deleted = folder_sync.retire_and_delete_kb(kb_id)\n    if not deleted:\n        raise HTTPException(status_code = 404, detail = \"Knowledge base not found\")\n    try:\n        folder_sync.delete_retired_scope(store.kb_scope(kb_id))\n    except Exception:\n        logger.warning(\"failed to delete retired knowledge-base scope %s\", kb_id, exc_info = True)\n    return {\"ok\": True}\n\n\ndef _raise_if_scope_retired(scope: str, detail: str = \"Knowledge base is being deleted\") -> None:\n    if folder_sync.scope_retired(scope):\n        raise HTTPException(status_code = 409, detail = detail)\n\n\n@router.post(\"/knowledge-bases/{kb_id}/documents\")\nasync def upload_kb_document(\n    kb_id: str,\n    file: UploadFile | None = File(None),\n    native_path_lease: str | None = Form(None, alias = \"nativePathLease\"),\n    ocr: bool | None = Form(None),\n    caption: bool | None = Form(None),\n    subject: str = Depends(get_current_subject),\n) -> dict:\n    _require_rag()\n    conn = _rag_connection()\n    try:\n        if store.get_kb(conn, kb_id) is None:\n            raise HTTPException(status_code = 404, detail = \"Knowledge base not found\")\n    finally:\n        conn.close()","sourceCodeStart":461,"sourceCodeEnd":497,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/rag.py#L461-L497","documentation":"_raise_if_scope_retired() returns HTTP 409 'Knowledge base is being deleted' when folder_sync.scope_retired(scope) is true: the KB's scope was retired by a delete in flight (retire_and_delete_kb marks the scope before/while removing data and stopping sync jobs). Writes to that scope — e.g. uploading a document — are rejected until deletion completes, rather than racing the deleter.","triggerScenarios":"Uploading a document or linking a folder to a KB that another request is currently deleting: the delete retires the scope, and any concurrent mutation to kb_scope(kb_id) hits the 409.","commonSituations":"Delete and upload tabs open simultaneously; automated import running while a user deletes the KB; slow scope cleanup (many documents) leaving the retired window open for seconds.","solutions":["Do not retry: propagate 'KB is being deleted' to the UI and drop the pending upload.","Close/disable upload UI for a KB as soon as its delete is initiated (optimistically mark it deleting).","On 409, refresh the KB list and continue against a different KB."],"exampleFix":"// before\nif (res.status === 409) retry(500); // pointless: scope is retiring\n// after\nif (res.status === 409) { ui.markKbDeleting(kbId); discardPendingUpload(); return; }","handlingStrategy":"validation","validationCode":"if (ui.kbState[kbId] === 'deleting') { abortPendingUpload(); return; } // set optimistically when delete starts","typeGuard":null,"tryCatchPattern":"if (e.status === 409 && e.detail === 'Knowledge base is being deleted') { markKbDeleting(kbId); discardPendingUpload(); }","preventionTips":["Mark a KB as deleting client-side the moment its delete is issued; block further writes to its scope.","Never retry on 409 here — the scope is going away.","Queue uploads only for KBs whose delete state is clear."],"tags":["http-409","knowledge-base","delete-race","concurrency","rag","studio-backend"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}