{"record":{"id":"06e2758198ccdd0d","repo":"infiniflow/ragflow","slug":"failed-to-create-skill-folder","errorCode":null,"errorMessage":"Failed to create skill folder","messagePattern":"Failed to create skill folder","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/pages/skills/hooks.ts","lineNumber":818,"sourceCode":"              if (existingVersion) {\n                message.error(\n                  t('skills.versionExists') || 'This version already exists',\n                );\n                return false;\n              }\n            }\n\n            skillFolderId = existingSkill.id;\n          } else {\n            // Create skill folder\n            const folderRes = await fileManagerService.createFolder({\n              name: skillNameNormalized,\n              type: 'folder',\n              parent_id: spaceFolderId,\n            });\n\n            if (folderRes.data.code !== 0) {\n              throw new Error('Failed to create skill folder');\n            }\n\n            skillFolderId = folderRes.data.data?.id;\n          }\n        } else {\n          throw new Error('Failed to list skills folder');\n        }\n\n        if (!skillFolderId) throw new Error('Failed to get skill folder ID');\n\n        // Create version folder\n        const versionRes = await fileManagerService.createFolder({\n          name: version,\n          type: 'folder',\n          parent_id: skillFolderId,\n        });\n\n        if (versionRes.data.code !== 0) {","sourceCodeStart":800,"sourceCodeEnd":836,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/web/src/pages/skills/hooks.ts#L800-L836","documentation":"Thrown in web/src/pages/skills/hooks.ts:818 when fileManagerService.createFolder for the skill's top-level folder returns a non-zero business code. The HTTP call resolved, but the backend refused to create the folder — most commonly because a folder with the same name already exists under the space folder (a race with the existence check), or the parent_id is invalid/expired.","triggerScenarios":"Two uploads of the same skill name in parallel: both pass the exists-check, the second create fails with 'already exists'; parent spaceFolderId deleted between steps; name rejected by backend sanitization; permission denied on the parent folder.","commonSituations":"Double-click on Upload. Uploading a skill that exists while the earlier listing was cached/stale. Space folder removed by another user mid-upload.","solutions":["Treat 'already exists' code as success: re-list and reuse the existing folder id instead of throwing","Handle the specific response message from createFolder in the error text","Prevent duplicate submissions by disabling the upload button while a mutation is in flight","Verify spaceFolderId is still valid (re-list) before creating"],"exampleFix":"// before\nif (folderRes.data.code !== 0) {\n  throw new Error('Failed to create skill folder');\n}\n\n// after\nif (folderRes.data.code !== 0) {\n  const { data: retryList } = await fileManagerService.listFile({\n    parent_id: spaceFolderId,\n  });\n  const existing = (retryList.data?.files || []).find(\n    (f: any) => f.name === skillNameNormalized && f.type === 'folder',\n  );\n  if (!existing) {\n    throw new Error(\n      folderRes.data.message || 'Failed to create skill folder',\n    );\n  }\n  skillFolderId = existing.id;\n}","handlingStrategy":"try-catch","validationCode":"const folderNameFree = async (parentId: string, name: string) => {\n  const { data } = await fileManagerService.listFile({ parent_id: parentId });\n  return !(data.data?.files || []).some((f) => f.name === name && f.type === 'folder');\n};","typeGuard":null,"tryCatchPattern":"try {\n  const res = await fileManagerService.createFolder({...});\n  if (res.data.code !== 0) throw new Error(res.data.message);\n} catch (e) {\n  if (/exists/i.test(e.message)) { /* re-list and reuse */ }\n  else throw e;\n}","preventionTips":["Disable the upload button while an upload is in flight to prevent duplicate creates","Treat 'already exists' as reuse rather than failure"],"tags":["skills","file-manager","upload","race-condition"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}