siyuan-note/siyuan · critical

skill storage is not a regular directory

Error message

skill storage is not a regular directory

What it means

openManagedSkillsRoot walks data/storage/ai/agent/skills creating each level and Lstats it; if any level exists but is not a real directory (a regular file, or a symlink), the error is thrown. This guards against storage layout tampering and symlink redirection of skill management operations.

Solutions

  1. Inspect data/storage/ai/agent/skills (and each parent) — remove or rename the file/symlink and create a real directory in its place
  2. Replace the symlink with a real directory (copy contents back if the symlink pointed at your data)
  3. If a stray file occupies a path meant to be a directory, delete it so Mkdir can recreate the level
  4. Restore the storage subtree from a good backup if the layout is corrupted

Example fix

// before
$ ls -l data/storage/ai/agent
skills -> /mnt/cloud/skills   # symlink

// after
$ rm data/storage/ai/agent/skills
$ mkdir data/storage/ai/agent/skills
$ cp /mnt/cloud/skills/* data/storage/ai/agent/skills/
Defensive patterns

Strategy: try-catch

Validate before calling

const st = await fs.lstat('data/storage/ai/agent/skills');
if (!st.isDirectory() || st.isSymbolicLink()) {
  throw new Error('skills storage must be a real directory');
}

Try / catch

try {
  await api.manageSkillFiles({action: 'list', path: '.'});
} catch (e) {
  if (e.message.includes('not a regular directory')) {
    // fix storage layout: replace file/symlink with a real directory
  }
}

Prevention

When it happens

Trigger: Any ManageSkillFiles action or TestSkillManagementCommitConflict when data/storage, data/storage/ai, data/storage/ai/agent, or data/storage/ai/agent/skills is a file or a symlink instead of a directory.

Common situations: A user/script replaced 'skills' with a symlink to a synced folder; a stray file named 'ai' was created inside storage; an interrupted migration left a file where a directory belongs; restoring data from a partial backup.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/0a505724b626b1b4. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/skill_manage.go:96

}

// 从工作空间目录逐层打开根目录,拒绝把管理操作重定向到链接目标。
func openManagedSkillsRoot() (*os.Root, error) {
	workspace, err := os.OpenRoot(DataDir)
	if err != nil {
		return nil, err
	}
	defer workspace.Close()
	for _, p := range []string{"storage", "storage/ai", "storage/ai/agent", "storage/ai/agent/skills"} {
		if err = workspace.Mkdir(p, 0755); err != nil && !errors.Is(err, os.ErrExist) {
			return nil, err
		}
		info, statErr := workspace.Lstat(p)
		if statErr != nil {
			return nil, statErr
		}
		if !info.IsDir() || info.Mode()&os.ModeSymlink != 0 {
			return nil, errors.New("skill storage is not a regular directory")
		}
	}
	return workspace.OpenRoot("storage/ai/agent/skills")
}

func checkManagedSkillPath(root *os.Root, p string) error {
	if err := validateManagedSkillPath(p); err != nil {
		return err
	}
	parts := strings.Split(p, "/")
	for i := range parts {
		info, err := root.Lstat(strings.Join(parts[:i+1], "/"))
		if errors.Is(err, os.ErrNotExist) && i == len(parts)-1 {
			return nil
		}
		if err != nil {
			return err
		}

View on GitHub (pinned to 9f775e8a12)