siyuan-note/siyuan · error

skill resource is not valid UTF-8: %s/%s

Error message

skill resource is not valid UTF-8: %s/%s

What it means

Skill resources are returned to callers as UTF-8 text, so readSkillResource validates the raw bytes with utf8.Valid before returning. If the file contains binary data or non-UTF-8 text (Latin-1, GBK, UTF-16), the library rejects it rather than returning mojibake. Only textual resources encoded as UTF-8 are supported.

Source

Thrown at kernel/util/skill.go:391

	}
	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()
	abs := filepath.Join(dir, name)
	if !gulu.File.IsSubPath(dir, abs) {
		return fmt.Errorf("invalid skill name: %s", name)
	}
	return nil
}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Convert the resource to UTF-8 encoding (iconv -f GBK -t UTF-8, or editor 'Save with encoding: UTF-8')
  2. Replace UTF-16 files with UTF-8 versions (many Windows 'Unicode' saves are UTF-16)
  3. Move binary assets (images, zip, PDF) out of the skill's text resources — they cannot be served as skill text
  4. Check the file type with `file <path>` to confirm it is text before adding it as a resource

Example fix

# before
$ file data.txt
data.txt: ISO-8859 text
# after
$ iconv -f ISO-8859-1 -t UTF-8 data.txt > data-utf8.txt
$ mv data-utf8.txt data.txt
Defensive patterns

Strategy: validation

Validate before calling

// Go: validate UTF-8 before saving a resource
if !utf8.Valid(content) {
    return fmt.Errorf("resource must be UTF-8 text")
}

Prevention

When it happens

Trigger: LoadSkill -> readSkillResource: the resource file passes the size check but its bytes fail utf8.Valid — a binary file (image, zip, PDF), UTF-16 text with BOM, or legacy-encoded text (GBK/Shift-JIS/Latin-1) saved as a skill resource.

Common situations: Exporting files from Windows tools that default to ANSI/GBK encoding; storing images or archives inside a skill folder; editors saving with a non-UTF-8 charset; files downloaded in binary vs text mode.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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