infiniflow/ragflow · error · Error

Skill not found

Error message

Skill not found

What it means

Thrown in web/src/pages/skills/hooks.ts:1043 when deleteSkill cannot resolve a target folder id after all fallbacks: the caller-supplied folderId, the _folderId stashed on the in-state skill, and a listFile search for a folder whose name equals skillId all failed. The skill's file-manager folder is what gets deleted, so without it the flow cannot proceed and reports 'Skill not found'.

Source

Thrown at web/src/pages/skills/hooks.ts:1043

          );
          if (spaceFolderId) {
            const { data: listData } = await fileManagerService.listFile({
              parent_id: spaceFolderId,
            });

            if (listData.code === 0) {
              const skillFolder = (listData.data?.files || []).find(
                (f: any) => f.type === 'folder' && f.name === skillId,
              );
              if (skillFolder) {
                targetFolderId = skillFolder.id;
              }
            }
          }
        }

        if (!targetFolderId) {
          throw new Error('Skill not found');
        }

        // Get versions by listing the skill folder
        const { data: versionData } = await fileManagerService.listFile({
          parent_id: targetFolderId,
        });

        let versionsToDelete: string[] = ['latest'];
        if (versionData.code === 0) {
          const versionFolders = (versionData.data?.files || []).filter(
            (f: any) => f.type === 'folder' && /^\d+\.\d+\.\d+/.test(f.name),
          );
          if (versionFolders.length > 0) {
            versionsToDelete = versionFolders.map((f: any) => f.name);
          }
        }

        // Delete search index for all versions

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Compare the folder-name normalization (lowercase, dashes) between deleteSkill's lookup and uploadSkill's creation
  2. Persist folderId on the skill record at fetch time so deletion never needs name guessing
  3. If folder resolution fails but the skill exists in the index, still remove the index entry instead of aborting entirely
  4. Re-fetch the skills list (fetchSkills) so _folderId is fresh, then retry delete

Example fix

// before
if (!targetFolderId) {
  throw new Error('Skill not found');
}

// after
if (!targetFolderId) {
  const normalized = skillId.replace(/\s+/g, '-').toLowerCase();
  if (normalized !== skillId) {
    const skillFolder = (listData.data?.files || []).find(
      (f: any) => f.type === 'folder' && f.name === normalized,
    );
    if (skillFolder) targetFolderId = skillFolder.id;
  }
}
if (!targetFolderId) {
  throw new Error(
    `Skill not found (skillId=${skillId}, space=${normalizedSpaceName})`,
  );
}
Defensive patterns

Strategy: type-guard

Validate before calling

const skillFolderIdFor = (skills: Skill[], skillId: string) =>
  skills.find((s) => s.id === skillId)?.['_folderId'] ?? null;

Type guard

const hasFolderRef = (s: any): s is Skill & { _folderId: string } =>
  typeof s?._folderId === 'string' && s._folderId.length > 0;

Try / catch

try {
  await deleteSkill(...);
} catch (e) {
  if (e.message === 'Skill not found') await fetchSkills(spaceName, spaceId); // resync
  else throw e;
}

Prevention

When it happens

Trigger: The skill appears in the list (from the search index) but its backing folder was already deleted or renamed in the file manager; skillId is the index's id, which differs from the folder name so the name-based lookup misses; the skills state was refetched from index-only data without _folderId; listFile returned code !== 0 so the search branch was skipped.

Common situations: Index out of sync with the file manager after a partial manual cleanup. Skill names containing spaces/uppercase normalized differently for the folder (name.replace(/\s+/g,'-').toLowerCase()) so the lookup name does not match. Stale component state across space switches.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/c4b42e89f17b5573. Report an issue: GitHub.