siyuan-note/siyuan · error
skill resource read failed: %s/%s
Error message
skill resource read failed: %s/%s
What it means
After opening the resource file, readSkillResource reads up to maxSkillResourceBytes+1 bytes with io.ReadAll; a non-EOF read error is reported as 'skill resource read failed'. This indicates an I/O problem while reading an already-open file, not a missing file or a size-limit rejection (those have distinct messages).
Source
Thrown at kernel/util/skill.go:385
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) {
return "", fmt.Errorf("skill resource is not valid UTF-8: %s/%s", skillName, resource)
}
return string(data), nil
}
func validateSkillName(name string) error {
if name == "" || name == "." || name == ".." {
return fmt.Errorf("invalid skill name: %s", name)
}
if strings.ContainsAny(name, `/\`) {
return fmt.Errorf("invalid skill name: %s", name)
}
dir := SkillsDir()View on GitHub (pinned to 8641553a1f)
Solutions
- Check disk health and free space on the volume holding the workspace (dmesg / smartctl / df)
- Retry the operation — transient I/O errors on network mounts often clear after remount
- Verify the file is intact by reading it manually (cat/head) and restore from backup or re-download the skill if corrupt
- Reinstall the skill (RemoveSkill then re-save/install) if the resource file is corrupted
Defensive patterns
Strategy: retry
Validate before calling
// Go: sanity-check readability before the API
f, err := os.Open(path)
if err != nil { return err }
b := make([]byte, 512)
_, rerr := f.Read(b)
f.Close()
if rerr != nil && rerr != io.EOF { return rerr } Try / catch
if _, err := LoadSkill(skill, res); err != nil {
if strings.Contains(err.Error(), "skill resource read failed") {
time.Sleep(100 * time.Millisecond)
return retryLoad(skill, res, 1) // bounded retry for transient I/O
}
return err
} Prevention
- Monitor disk health and free space on the workspace volume
- Avoid storing skill resources on flaky network mounts
- Restore corrupted skill directories from source control or backups
When it happens
Trigger: LoadSkill -> readSkillResource: io.ReadAll on the opened resource file returns an error — disk I/O error, bad sectors, network-mounted file becoming unavailable mid-read, or a file descriptor issue.
Common situations: Failing or full disk hosting the workspace; NFS/SMB mount dropped while reading; storage device detached on mobile/external drives; extremely rare kernel/file-descriptor errors after Open succeeded.
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
- read session file failed: %w
- create session dir failed: %w
- save session file failed: %w
- read history dir failed: %w
- read inline styles failed: %w
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/719c4b9448ad5edd.
Report an issue: GitHub.