{"record":{"id":"770b28aab9b1f59c","repo":"siyuan-note/siyuan","slug":"skill-resource-is-not-a-regular-file-s-s","errorCode":null,"errorMessage":"skill resource is not a regular file: %s/%s","messagePattern":"skill resource is not a regular file: (.+?)/(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/skill.go","lineNumber":372,"sourceCode":"\nfunc readSkillResource(skillDir, skillName, resource string) (string, error) {\n\trealRoot, err := filepath.EvalSymlinks(skillDir)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"skill not found: %s\", skillName)\n\t}\n\n\ttarget := filepath.Join(realRoot, filepath.FromSlash(resource))\n\trealTarget, err := filepath.EvalSymlinks(target)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"skill resource not found: %s/%s\", skillName, resource)\n\t}\n\tif realTarget != realRoot && !gulu.File.IsSubPath(realRoot, realTarget) {\n\t\treturn \"\", fmt.Errorf(\"skill resource escapes skill directory: %s\", resource)\n\t}\n\n\tinfo, err := os.Stat(realTarget)\n\tif err != nil || !info.Mode().IsRegular() {\n\t\treturn \"\", fmt.Errorf(\"skill resource is not a regular file: %s/%s\", skillName, resource)\n\t}\n\tif info.Size() > maxSkillResourceBytes {\n\t\treturn \"\", fmt.Errorf(\"skill resource exceeds the %d byte limit: %s/%s\", maxSkillResourceBytes, skillName, resource)\n\t}\n\n\tfile, err := os.Open(realTarget)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"skill resource not found: %s/%s\", skillName, resource)\n\t}\n\tdefer file.Close()\n\tdata, err := io.ReadAll(io.LimitReader(file, maxSkillResourceBytes+1))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"skill resource read failed: %s/%s\", skillName, resource)\n\t}\n\tif len(data) > maxSkillResourceBytes {\n\t\treturn \"\", fmt.Errorf(\"skill resource exceeds the %d byte limit: %s/%s\", maxSkillResourceBytes, skillName, resource)\n\t}\n\tif !utf8.Valid(data) {","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/skill.go#L354-L390","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nLoadSkill(\"my-skill/assets\", enabled) // assets is a directory\n// after\nLoadSkill(\"my-skill/assets/diagram.svg\", enabled)","handlingStrategy":"validation","validationCode":"const stat = require(\"fs\").statSync(p.join(skillDir, resource));\nif (!stat.isFile()) throw new Error(\"resource must be a regular file\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["skills","filesystem","validation"],"backgroundTag":"file-not-found","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}