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
- Convert the resource to UTF-8 encoding (iconv -f GBK -t UTF-8, or editor 'Save with encoding: UTF-8')
- Replace UTF-16 files with UTF-8 versions (many Windows 'Unicode' saves are UTF-16)
- Move binary assets (images, zip, PDF) out of the skill's text resources — they cannot be served as skill text
- 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
- Save all skill files with UTF-8 encoding; convert legacy encodings with iconv first
- Never place binary assets (images, archives) in a skill's text resources
- Run `file <path>` to confirm text encoding before adding a resource
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
- 344
- template source is not UTF-8
- invalid template source
- invalid UTF-8 path
- skill resource read failed: %s/%s
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/19c8f5150a7fde7c.
Report an issue: GitHub.