siyuan-note/siyuan · error

skill path is not a regular file or directory

Error message

skill path is not a regular file or directory

What it means

checkManagedSkillPath validates each ancestor component of a managed-skill path under data/storage/ai/agent/skills. This error means an existing path component is neither a symlink-free directory nor a regular file (e.g. a symlink, FIFO, device or socket), so skill file operations refuse to touch it. The library enforces this to prevent link redirection and special-file side effects inside the skill storage root.

Solutions

  1. Inspect the reported path under data/storage/ai/agent/skills with ls -l (or equivalent) and delete or replace the symlink/special file with a real directory or file
  2. Copy the actual skill content into a real directory instead of linking it
  3. Re-run the skill management operation after the path contains only regular files and directories

Example fix

// before (shell, in workspace)
ln -s ~/shared-skills/my-skill data/storage/ai/agent/skills/my-skill
// after
cp -r ~/shared-skills/my-skill data/storage/ai/agent/skills/my-skill
Defensive patterns

Strategy: validation

Validate before calling

func pathIsClean(root *os.Root, p string) error {
  info, err := root.Lstat(p)
  if err != nil { return err }
  if info.Mode()&os.ModeSymlink != 0 || (!info.IsDir() && !info.Mode().IsRegular()) {
    return errors.New("symlink or special file at " + p)
  }
  return nil
}

Type guard

func isRegularOrDir(info os.FileInfo) bool {
  return info.Mode()&os.ModeSymlink == 0 && (info.IsDir() || info.Mode().IsRegular())
}

Try / catch

if err := ManageSkillFiles(req); err != nil && strings.Contains(err.Error(), "not a regular file or directory") {
  // surface guidance: remove symlinks/special files from skill storage
}

Prevention

When it happens

Trigger: Any ManageSkillFiles action (list/read/write/create/mkdir/move/remove) whose request.Path, Target, or an intermediate path component resolves to a symlink or special file (non-regular, non-directory) via root.Lstat during checkManagedSkillPath; also re-checked after writeManagedSkillSource writes the temp file, so the target being swapped to a link mid-operation also raises it.

Common situations: A user manually created symlinks inside storage/ai/agent/skills to share skills between workspaces; a synced/copied directory contains macOS Finder aliases or Windows junction points; a leftover FIFO or socket from another tool occupies a skill name.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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

Appendix: source

Thrown at kernel/util/skill_manage.go:116

	}
	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
		}
		if info.Mode()&os.ModeSymlink != 0 || (!info.IsDir() && !info.Mode().IsRegular()) {
			return errors.New("skill path is not a regular file or directory")
		}
	}
	return nil
}

func isManagedSkillManifest(p string) bool {
	return strings.Count(p, "/") == 1 && strings.EqualFold(path.Base(p), "SKILL.md")
}

// 目录版本包含每个资源的内容哈希,保持大小和修改时间不变的外部编辑也会产生冲突。
func managedSkillRevision(root *os.Root, p string) (string, error) {
	h := sha256.New()
	err := fs.WalkDir(root.FS(), p, func(name string, entry fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		info, err := entry.Info()
		if err != nil {

View on GitHub (pinned to 9f775e8a12)