siyuan-note/siyuan · error
skill source is not a regular file
Error message
skill source is not a regular file
What it means
readManagedSkillSource opens a skill file and Stats it before reading; this error means the opened file descriptor does not refer to a regular file (e.g. a FIFO, device or special file). The library only returns UTF-8 text content to the editor, so non-regular sources are rejected before any read occurs.
Solutions
- Replace the special file at the path with a regular text file containing the skill content
- Identify which process created the special file and stop it from writing into data/storage/ai/agent/skills
- Re-create the file via the skill management API (action "write") instead of manually
Example fix
// before (accidentally a FIFO) mkfifo data/storage/ai/agent/skills/my-skill/SKILL.md // after printf -- '---\nname: my-skill\ndescription: x\n---\n' > data/storage/ai/agent/skills/my-skill/SKILL.md
Defensive patterns
Strategy: type-guard
Validate before calling
info, err := os.Stat(filepath.Join(dataDir, "storage/ai/agent/skills", p))
if err == nil && !info.Mode().IsRegular() {
// replace with a regular file before calling read
} Type guard
func isRegularFile(info os.FileInfo) bool { return info.Mode().IsRegular() } Try / catch
if err := ManageSkillFiles(req); err != nil && strings.Contains(err.Error(), "not a regular file") {
// recreate the path as a regular text file
} Prevention
- Create skill files only through the skill management API
- Don't place FIFOs/devices/sockets inside workspace data
- Check file types after restoring from unusual backups
When it happens
Trigger: ManageSkillFiles read (or write/move/remove pre-checks) on a path that passed validateManagedSkillPath but whose root.Open(...).Stat() reports a non-regular mode — typically a FIFO, /dev-like node or similar placed at a skill file path; called during list to compute Editable, and before move/remove of SKILL.md.
Common situations: A special file was created at the skill path by another process or a misconfigured tooling script; a name that looks like a file is actually a named pipe set up for IPC inside the workspace.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- skill already exists
- skill directory already exists or is invalid
- skill directory contains a link or special file
- skill path is not a regular file or directory
- 345
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/5508b303913a8435.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/skill_manage.go:213
return "encoding"
case errors.Is(err, ErrSkillTooLarge):
return "tooLarge"
}
return ""
}
func readManagedSkillSource(root *os.Root, p string) (string, error) {
file, err := root.Open(p)
if err != nil {
return "", err
}
defer file.Close()
info, err := file.Stat()
if err != nil {
return "", err
}
if !info.Mode().IsRegular() {
return "", errors.New("skill source is not a regular file")
}
if info.Size() > maxManagedSkillSourceSize {
return "", ErrSkillTooLarge
}
content, err := io.ReadAll(io.LimitReader(file, maxManagedSkillSourceSize+1))
if err != nil {
return "", err
}
// UTF-8 BOM 随正文保留;二进制内容不会返回给文本编辑器。
if err = validateManagedSkillSource(string(content)); err != nil {
return "", err
}
return string(content), nil
}
// 写入临时文件并同步后替换,发生写入错误时保留原文及其换行和元数据。
func writeManagedSkillSource(root *os.Root, p, content string, create bool, revision string) error {
if err := validateManagedSkillSource(content); err != nil {View on GitHub (pinned to 9f775e8a12)