siyuan-note/siyuan · error
skill resource is not a regular file: %s/%s
Error message
skill resource is not a regular file: %s/%s
What it means
readSkillResource only serves regular files: after stat, if the target is missing, unreadable, or not a regular file (directory, FIFO, device, socket), it returns this error. Directories within a skill must not be requested as resources.
Source
Thrown at kernel/util/skill.go:372
func readSkillResource(skillDir, skillName, resource string) (string, error) {
realRoot, err := filepath.EvalSymlinks(skillDir)
if err != nil {
return "", fmt.Errorf("skill not found: %s", skillName)
}
target := filepath.Join(realRoot, filepath.FromSlash(resource))
realTarget, err := filepath.EvalSymlinks(target)
if err != nil {
return "", fmt.Errorf("skill resource not found: %s/%s", skillName, resource)
}
if realTarget != realRoot && !gulu.File.IsSubPath(realRoot, realTarget) {
return "", fmt.Errorf("skill resource escapes skill directory: %s", resource)
}
info, err := os.Stat(realTarget)
if err != nil || !info.Mode().IsRegular() {
return "", fmt.Errorf("skill resource is not a regular file: %s/%s", skillName, resource)
}
if info.Size() > maxSkillResourceBytes {
return "", fmt.Errorf("skill resource exceeds the %d byte limit: %s/%s", maxSkillResourceBytes, skillName, resource)
}
file, err := os.Open(realTarget)
if err != nil {
return "", fmt.Errorf("skill resource not found: %s/%s", skillName, resource)
}
defer file.Close()
data, err := io.ReadAll(io.LimitReader(file, maxSkillResourceBytes+1))
if err != nil {
return "", fmt.Errorf("skill resource read failed: %s/%s", skillName, resource)
}
if len(data) > maxSkillResourceBytes {
return "", fmt.Errorf("skill resource exceeds the %d byte limit: %s/%s", maxSkillResourceBytes, skillName, resource)
}
if !utf8.Valid(data) {View on GitHub (pinned to 8641553a1f)
Solutions
- Append the actual filename — resource locators must point at files, not directories
- List the skill directory to find the concrete file to request
- Remove or replace any non-regular files (FIFOs, sockets) inside the skill directory
- Fix permissions so the kernel can stat and read the file
Example fix
// before
LoadSkill("my-skill/assets", enabled) // assets is a directory
// after
LoadSkill("my-skill/assets/diagram.svg", enabled) Defensive patterns
Strategy: validation
Validate before calling
const stat = require("fs").statSync(p.join(skillDir, resource));
if (!stat.isFile()) throw new Error("resource must be a regular file"); Prevention
- Only request concrete file paths, never directory paths
- List skill directory contents when unsure of the exact resource filename
- Keep FIFOs, sockets, and devices out of skill directories
When it happens
Trigger: Calling LoadSkill with 'skill/dirname' where dirname is a directory; requesting a FIFO/device/socket path inside the skill; stat failure due to permissions on the resolved target.
Common situations: Agent guesses a directory path instead of a file path; requesting 'assets/' style paths; special files accidentally created inside a skill directory by other tooling.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- export source [%s] is not a regular file
- asset path does not belong to a notebook: %s
- export artifact [%s] is a directory
- inspect imported notebook [%s] failed: %w
- boxID mismatch: param=%s, path=%s
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/770b28aab9b1f59c.
Report an issue: GitHub.