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

  1. Check disk health and free space on the volume holding the workspace (dmesg / smartctl / df)
  2. Retry the operation — transient I/O errors on network mounts often clear after remount
  3. Verify the file is intact by reading it manually (cat/head) and restore from backup or re-download the skill if corrupt
  4. 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

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


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/719c4b9448ad5edd. Report an issue: GitHub.