siyuan-note/siyuan · error
invalid skill name
Error message
invalid skill name: %s
What it means
validateSkillName rejects skill names that are empty, '.' or '..'. It is the first gate for all skill write operations (Read/Save/Remove/Rename/install), preventing degenerate names from reaching filesystem joins.
Solutions
- Provide a non-empty skill name before calling the API
- Filter out '.' and '..' when iterating directory entries to build skill names
- Trim and validate the name at the caller boundary (e.g. strings.TrimSpace, reject if empty)
Example fix
// before
entries, _ := os.ReadDir(util.SkillsDir())
for _, e := range entries {
util.SaveSkill(e.Name(), content) // panics on "." / ".."
}
// after
for _, e := range entries {
if e.IsDir() && e.Name() != "." && e.Name() != ".." {
util.SaveSkill(e.Name(), content)
}
} Defensive patterns
Strategy: validation
Validate before calling
func validName(n string) bool {
return n != "" && n != "." && n != ".."
} Try / catch
if err := util.ValidateSkillName(name); err != nil { /* reject before calling SaveSkill */ } Prevention
- Trim and require non-empty names at the caller boundary
- Filter '.' and '..' when iterating directory entries
- Validate names before any skill API call
When it happens
Trigger: ReadSkill, SaveSkill, RemoveSkill, RenameSkill, installSkillDirs, or installFromSingleSkillMD is called with an empty name, ".", or "..".
Common situations: A name variable left unset before calling SaveSkill; a loop iterating directory entries that passes '.'/'..' entries through unfiltered; an API caller omitting the skill name field.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- document IDs are required
- invalid attribute view source
- invalid package type
- invalid paste row count
- invalid reorder position
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/5e257eff0e2e4dce.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/skill.go:398
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
}
func ReadSkill(name string, enabledUserSkills []string) (string, error) {
if err := validateSkillName(name); err != nil {
return "", err
}
if record, ok := findSkillRecord(resolveSkillRecords(enabledUserSkills), name); ok {
return record.Content, nilView on GitHub (pinned to 9f775e8a12)